summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2020-12-30 19:07:31 +0000
committerJuan J. Martinez <jjm@usebox.net>2020-12-30 19:23:41 +0000
commit2682bc5d1d864341aaeb42a449db73c3ecd16d70 (patch)
tree9116764364b4ee0ce7f6037305077807b57776de
downloadubox-msx-lib-1.0.tar.gz
ubox-msx-lib-1.0.zip
Initial import1.0
-rw-r--r--.gitignore29
-rw-r--r--CHANGES.md6
-rw-r--r--COPYING20
-rw-r--r--Makefile35
-rw-r--r--README.md109
-rw-r--r--TODO.md10
-rw-r--r--docs/Makefile18
-rw-r--r--docs/footer.md3
-rw-r--r--docs/monokai.css73
-rw-r--r--docs/mplayer.md83
-rw-r--r--docs/spman.md36
-rw-r--r--docs/water.css2
-rw-r--r--game/Makefile12
-rw-r--r--game/README.md37
-rw-r--r--game/data/Makefile21
-rw-r--r--game/data/effects.aksbin0 -> 1614 bytes
-rw-r--r--game/data/enemy.pngbin0 -> 5448 bytes
-rw-r--r--game/data/map.json159
-rw-r--r--game/data/map_conf.json19
-rw-r--r--game/data/player.pngbin0 -> 5447 bytes
-rw-r--r--game/data/song.aksbin0 -> 2019 bytes
-rw-r--r--game/data/tiles.pngbin0 -> 6932 bytes
-rw-r--r--game/src/Makefile48
-rw-r--r--game/src/akm.z8015
-rw-r--r--game/src/aux.c27
-rw-r--r--game/src/aux.h13
-rw-r--r--game/src/crt0.z8082
-rw-r--r--game/src/data.c9
-rw-r--r--game/src/effects.asm249
-rw-r--r--game/src/effects_playerconfig.asm14
-rw-r--r--game/src/game.c437
-rw-r--r--game/src/game.h130
-rw-r--r--game/src/main.c143
-rw-r--r--game/src/main.h50
-rw-r--r--game/src/song.asm650
-rw-r--r--game/src/song_playerconfig.asm17
-rw-r--r--include/mplayer.h181
-rw-r--r--include/spman.h149
-rw-r--r--include/ubox.h668
-rw-r--r--src/mplayer/Makefile19
-rw-r--r--src/mplayer/akm/LICENSE.txt21
-rw-r--r--src/mplayer/akm/Makefile4
-rw-r--r--src/mplayer/akm/PlayerAkm.asm2328
-rw-r--r--src/mplayer/akm/PlayerAkm_SoundEffects.asm477
-rw-r--r--src/mplayer/akm/README.md10
-rw-r--r--src/mplayer/akm/akm_ubox.asm27
-rw-r--r--src/mplayer/mplayer_init.z8013
-rw-r--r--src/mplayer/mplayer_init_effects.z8016
-rw-r--r--src/mplayer/mplayer_is_sound_effect_on.z809
-rw-r--r--src/mplayer/mplayer_play.z809
-rw-r--r--src/mplayer/mplayer_play_effect.z8018
-rw-r--r--src/mplayer/mplayer_play_effect_p.z8045
-rw-r--r--src/mplayer/mplayer_stop.z809
-rw-r--r--src/mplayer/mplayer_stop_effect_channel.z8010
-rw-r--r--src/spman/Makefile20
-rw-r--r--src/spman/spman.c118
-rw-r--r--src/ubox/Makefile19
-rw-r--r--src/ubox/ubox_disable_screen.z807
-rw-r--r--src/ubox/ubox_enable_screen.z807
-rw-r--r--src/ubox/ubox_fill_screen.z8010
-rw-r--r--src/ubox/ubox_get_tile.z8028
-rw-r--r--src/ubox/ubox_get_vsync_freq.z809
-rw-r--r--src/ubox/ubox_isr.z8079
-rw-r--r--src/ubox/ubox_put_tile.z8030
-rw-r--r--src/ubox/ubox_read_ctl.z80100
-rw-r--r--src/ubox/ubox_read_keys.z8010
-rw-r--r--src/ubox/ubox_read_vm.z8023
-rw-r--r--src/ubox/ubox_reset_tick.z8010
-rw-r--r--src/ubox/ubox_select_ctl.z8042
-rw-r--r--src/ubox/ubox_set_colors.z8023
-rw-r--r--src/ubox/ubox_set_mode.z808
-rw-r--r--src/ubox/ubox_set_sprite_attr.z8029
-rw-r--r--src/ubox/ubox_set_sprite_pat16.z8032
-rw-r--r--src/ubox/ubox_set_sprite_pat16_flip.z8063
-rw-r--r--src/ubox/ubox_set_sprite_pat8.z8030
-rw-r--r--src/ubox/ubox_set_sprite_pat8_flip.z8048
-rw-r--r--src/ubox/ubox_set_tiles.z8021
-rw-r--r--src/ubox/ubox_set_tiles_colors.z8021
-rw-r--r--src/ubox/ubox_set_user_isr.z808
-rw-r--r--src/ubox/ubox_wait.z8023
-rw-r--r--src/ubox/ubox_wait_for.z8014
-rw-r--r--src/ubox/ubox_write_vm.z8023
-rw-r--r--src/ubox/ubox_wvdp.z8014
-rw-r--r--tools/Makefile17
-rwxr-xr-xtools/chksize30
-rwxr-xr-xtools/hdoc.py157
-rwxr-xr-xtools/hex2bin-2.0/Makefile42
-rw-r--r--tools/hex2bin-2.0/doc/CRC list.txt542
-rw-r--r--tools/hex2bin-2.0/doc/ChangeLog_hex2bin57
-rw-r--r--tools/hex2bin-2.0/doc/ChangeLog_mot2bin49
-rw-r--r--tools/hex2bin-2.0/doc/README225
-rw-r--r--tools/hex2bin-2.0/doc/S-record.txt361
-rw-r--r--tools/hex2bin-2.0/doc/formats.txt72
-rw-r--r--tools/hex2bin-2.0/doc/intelhex.spc409
-rw-r--r--tools/hex2bin-2.0/doc/srec.txt447
-rw-r--r--tools/hex2bin-2.0/src/binary.c196
-rw-r--r--tools/hex2bin-2.0/src/binary.h36
-rw-r--r--tools/hex2bin-2.0/src/common.c527
-rw-r--r--tools/hex2bin-2.0/src/common.h116
-rw-r--r--tools/hex2bin-2.0/src/hex2bin.1294
-rw-r--r--tools/hex2bin-2.0/src/hex2bin.c587
-rw-r--r--tools/hex2bin-2.0/src/hex2bin.pod161
-rw-r--r--tools/hex2bin-2.0/src/libcrc.c204
-rw-r--r--tools/hex2bin-2.0/src/libcrc.h44
-rw-r--r--tools/hex2bin-2.0/src/mot2bin.c518
-rwxr-xr-xtools/map.py385
-rwxr-xr-xtools/pandocfilter-pygments.py37
-rwxr-xr-xtools/png2sprites.py138
-rwxr-xr-xtools/png2tiles.py190
-rw-r--r--tools/rasm/Makefile12
-rw-r--r--tools/rasm/decrunch/aplib_z80_todo.asm190
-rw-r--r--tools/rasm/decrunch/deexo.asm118
-rw-r--r--tools/rasm/decrunch/dzx7_turbo.asm80
-rw-r--r--tools/rasm/decrunch/exomizer3megachur.asm210
-rw-r--r--tools/rasm/decrunch/lz48decrunch_v006.asm113
-rw-r--r--tools/rasm/decrunch/lz49decrunch_v001.asm138
-rw-r--r--tools/rasm/decrunch/lz4_docent.asm118
-rw-r--r--tools/rasm/exomizer.h4942
-rw-r--r--tools/rasm/lz4.h3329
-rw-r--r--tools/rasm/minilib.h1151
-rw-r--r--tools/rasm/rasm.h26
-rw-r--r--tools/rasm/rasm_v0120.c17472
-rw-r--r--tools/rasm/zx7.h293
123 files changed, 41471 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..51f70b1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,29 @@
+*.o
+*.swp
+*~
+*.def
+*.bin
+*.map
+*.cdt
+*.dsk
+*.scr
+*.opt
+*.rel
+*.lk
+*.noi
+*.lst
+*.sym
+src/*/*.asm
+*.ihx
+*.ucl
+*.cas
+*.rom
+*.lib
+Makefile.deps
+bin/
+lib/
+game/bin
+game/generated
+game/build
+docs/reference.*
+docs/*.html
diff --git a/CHANGES.md b/CHANGES.md
new file mode 100644
index 0000000..7bf9933
--- /dev/null
+++ b/CHANGES.md
@@ -0,0 +1,6 @@
+2020-12-30 Juan J. Martinez <jjm@usebox.net>
+
+Release 1.0:
+
+ - initial import
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..9f88f99
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,20 @@
+ubox MSX lib
+Copyright (C) 2020 by Juan J. Martinez <jjm@usebox.net>
+
+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/Makefile b/Makefile
new file mode 100644
index 0000000..0fabf88
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,35 @@
+all: libs
+
+libs: ubox spman mplayer
+
+game: libs bin/game.rom
+
+bin/game.rom:
+ mkdir -p ./bin
+ make -C tools
+ make -C game
+
+docs:
+ make -C docs
+
+ubox:
+ mkdir -p lib
+ make -C src/ubox
+
+spman:
+ mkdir -p lib
+ make -C src/spman
+
+mplayer:
+ mkdir -p lib
+ make -C src/mplayer
+
+.PHONY: clean docs ubox libs bin/game.rom
+clean:
+ rm -rf ./bin
+ make -C src/ubox clean
+ make -C src/spman clean
+ make -C src/mplayer clean
+ make -C docs clean
+ make -C game clean
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..366fe4f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,109 @@
+# ubox MSX lib
+
+This is a set of libraries and tools to make MSX games using the C programming
+language.
+
+There are three components:
+
+ - **ubox**: thin wrapper around MSX 1 BIOS, focusing on Screen 2 and 32K
+ cartridge ROMs.
+ - **spman**: a simple sprite and pattern manager with flickering support.
+ - **mplayer**: a wrapper around Arkos 2 AKM player, supporting music and
+ priority based one channel sound effects.
+
+The aim is making MSX games in C, without writing Z80 assembler or having a
+deep knowledge of the system.
+
+## Requirements
+
+ - SDCC (3.9.0 recommended)
+ - GNU Make (others may work)
+ - a POSIX compatible environment
+
+If you want to build the example you will also need:
+
+ - python 3
+ - pillow
+ - GCC (only the C compiler)
+
+If you want to build the docs you will also need:
+
+ - pandoc
+ - python 3
+ - pygments
+ - pandocfilters
+
+## Building
+
+To build the libraries run:
+
+ make
+
+After a successful build, the libraries should be in `./lib`.
+
+The include files are ready to use in `./include`.
+
+Add those directories in `SDCC`'s search path and you are ready to go.
+
+### Building the example
+
+An example game is included with the libraries and it can be built with:
+
+ make game
+
+After a successful build, the game ROM should be in `./bin`.
+
+### Building the docs
+
+The documentation is available at
+[usebox.net](https://www.usebox.net/jjm/ubox-msx-lib/), so this is optional.
+
+To build the docs run:
+
+ make docs
+
+The reference in `HTML` format will be generated in `./docs`.
+
+## Contributing
+
+All contributions are welcome.
+
+If you think you have found a bug, please submit a bug report providing some
+information:
+
+ - What was expected to happen
+ - What actually happens
+ - How to reproduce the issue
+
+Some advice if you want to make a successful contribution:
+
+ - Be cordial
+ - Get early feedback, specially when working on a large contribution
+ - Contributions always require a pull request and a review
+
+## Authors
+
+This was mostly written by Juan J. Martinez during the development of
+[Night Knight](https://www.usebox.net/jjm/night-knight/) and
+[Uchūsen Gamma](https://www.usebox.net/jjm/uchusen-gamma/).
+
+ - Juan J. Martinez <jjm@usebox.net>
+ - Your name here?
+
+## Copying
+
+This software is distributed under MIT license, unless stated otherwise.
+
+See COPYING file.
+
+**TL;DR**: the only condition is that you are required to preserve the copyright
+and license notices. Licensed works, modifications, and larger works may be
+distributed under different terms and without source code; this includes any game
+made with the help of this software.
+
+Credit is appreciated, but is not a legal requirement. For example: you can add
+to the game's documentation a note like "This game uses ubox MSX lib".
+
+There are some third party tools included here for convenience and are covered
+by their own license or are public domain.
+
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..20f2015
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,10 @@
+Short/mid term:
+
+ - Add util tools/libs (e.g. compression)
+ - Improve MSX 1 support
+ - Support 48K ROMs
+ - CAS support
+
+Long term:
+ - Add MSX 2 support
+
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..83bb24b
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,18 @@
+all: msx-lib-ref.html spman-lib-ref.html mplayer-lib-ref.html
+
+msx-lib-ref.html: ../include/ubox.h ../tools/hdoc.py footer.md header.html
+ ../tools/hdoc.py --footer footer.md "ubox.lib reference" < ../include/ubox.h > reference.ubox.md
+ pandoc -s -t html5 -c water.css -c monokai.css --toc --no-highlight -F ../tools/pandocfilter-pygments.py -B header.html reference.ubox.md -o ubox-lib-ref.html
+
+spman-lib-ref.html: ../include/spman.h ../tools/hdoc.py footer.md spman.md header.html
+ ../tools/hdoc.py --header spman.md --footer footer.md "spman.lib reference" < ../include/spman.h > reference.spman.md
+ pandoc -s -t html5 -c water.css -c monokai.css --toc --no-highlight -F ../tools/pandocfilter-pygments.py -B header.html reference.spman.md -o spman-lib-ref.html
+
+mplayer-lib-ref.html: ../include/mplayer.h ../tools/hdoc.py footer.md mplayer.md header.html
+ ../tools/hdoc.py --header mplayer.md --footer footer.md "mplayer.lib reference" < ../include/mplayer.h > reference.mplayer.md
+ pandoc -s -t html5 -c water.css -c monokai.css --toc --no-highlight -F ../tools/pandocfilter-pygments.py -B header.html reference.mplayer.md -o mplayer-lib-ref.html
+
+clean:
+ rm -f ubox-lib-ref.html spman-lib-ref.html mplayer-lib-ref.html reference.ubox.md reference.spman.md reference.mplayer.md
+
+.PHONY: clean all
diff --git a/docs/footer.md b/docs/footer.md
new file mode 100644
index 0000000..276828c
--- /dev/null
+++ b/docs/footer.md
@@ -0,0 +1,3 @@
+-----
+**Copyright &copy; 2020 Juan J. Martinez (jjm at usebox.net)**
+Licensed [CC BY NC SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/).
diff --git a/docs/monokai.css b/docs/monokai.css
new file mode 100644
index 0000000..18556b5
--- /dev/null
+++ b/docs/monokai.css
@@ -0,0 +1,73 @@
+.highlight .hll { background-color: #49483e }
+.highlight { background: #272822; color: #f8f8f2 }
+.highlight .c { color: #75715e } /* Comment */
+.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
+.highlight .k { color: #66d9ef } /* Keyword */
+.highlight .l { color: #ae81ff } /* Literal */
+.highlight .n { color: #f8f8f2 } /* Name */
+.highlight .o { color: #f92672 } /* Operator */
+.highlight .p { color: #f8f8f2 } /* Punctuation */
+.highlight .ch { color: #75715e } /* Comment.Hashbang */
+.highlight .cm { color: #75715e } /* Comment.Multiline */
+.highlight .cp { color: #75715e } /* Comment.Preproc */
+.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
+.highlight .c1 { color: #75715e } /* Comment.Single */
+.highlight .cs { color: #75715e } /* Comment.Special */
+.highlight .gd { color: #f92672 } /* Generic.Deleted */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gi { color: #a6e22e } /* Generic.Inserted */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #75715e } /* Generic.Subheading */
+.highlight .kc { color: #66d9ef } /* Keyword.Constant */
+.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
+.highlight .kn { color: #f92672 } /* Keyword.Namespace */
+.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
+.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
+.highlight .kt { color: #66d9ef } /* Keyword.Type */
+.highlight .ld { color: #e6db74 } /* Literal.Date */
+.highlight .m { color: #ae81ff } /* Literal.Number */
+.highlight .s { color: #e6db74 } /* Literal.String */
+.highlight .na { color: #a6e22e } /* Name.Attribute */
+.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
+.highlight .nc { color: #a6e22e } /* Name.Class */
+.highlight .no { color: #66d9ef } /* Name.Constant */
+.highlight .nd { color: #a6e22e } /* Name.Decorator */
+.highlight .ni { color: #f8f8f2 } /* Name.Entity */
+.highlight .ne { color: #a6e22e } /* Name.Exception */
+.highlight .nf { color: #a6e22e } /* Name.Function */
+.highlight .nl { color: #f8f8f2 } /* Name.Label */
+.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
+.highlight .nx { color: #a6e22e } /* Name.Other */
+.highlight .py { color: #f8f8f2 } /* Name.Property */
+.highlight .nt { color: #f92672 } /* Name.Tag */
+.highlight .nv { color: #f8f8f2 } /* Name.Variable */
+.highlight .ow { color: #f92672 } /* Operator.Word */
+.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
+.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
+.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
+.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
+.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
+.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
+.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
+.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
+.highlight .sc { color: #e6db74 } /* Literal.String.Char */
+.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
+.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
+.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
+.highlight .se { color: #ae81ff } /* Literal.String.Escape */
+.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
+.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
+.highlight .sx { color: #e6db74 } /* Literal.String.Other */
+.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
+.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
+.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
+.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
+.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
+.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
+.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
+.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
+.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
+.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
+
+div.highlight{padding-left:16px;display:block;overflow-x:auto;border-radius:6px}
+nav>a{display:inline-block;margin-right: 1em}
diff --git a/docs/mplayer.md b/docs/mplayer.md
new file mode 100644
index 0000000..10e4689
--- /dev/null
+++ b/docs/mplayer.md
@@ -0,0 +1,83 @@
+## Overview
+
+**mplayer** provides easy access to Arkos 2 Minimal Player (AKM) ROM version
+from SDCC.
+
+The library adds support for priority based sound effects, so it is possible
+to have two uninterrupted channels for music and leave the third one for
+effects.
+
+When the priority effects are used, an effect is played if no effect is
+already being played or if the effect being played has less priority. The
+effect number is used as priority, being effect number 1 the one with lowest
+priority.
+
+The player comes with a generic configuration supporting all the features. If
+the songs/effects don't use everything that AKM can offer, it is possible to
+configure the player to remove features saving space and CPU.
+
+Arkos 2 Player is licensed MIT, please check `src/mplayer/akm/` directory for
+further info about configuring the player.
+
+Visit [Arkos Tracker website](http://www.julien-nevo.com/arkostracker/) to
+download the tracker.
+
+## Exporting songs and effects
+
+The player is quite complex and has a lot of options that can be disabled if
+not used, reducing space and CPU use.
+
+**mplayer** is only a fixed interface, and the player is compiled customised to
+the song and effects used by the game.
+
+Currently AKM can only be compiled with **rasm** (included in `./tools`), and then
+processed with `Disark` to generate ASM compatible with `SDCC`. Once that code is
+compiled, it will be relocated by the linker.
+
+Notes on **Disark**:
+
+ - It is not open source yet, so it needs to be put in the PATH. It is
+ distributed as part of Arkos 2 Traker tools.
+ - Because it uses uppercase labels, the variable exported and accessible from
+ C will be all uppercase. For exampler: for `song` we will use `SONG`.
+
+This is automated, and the only counter-intuitive step is making an ASM file
+for **rasm** such as:
+
+```
+;
+; to build the custom AKM player with song + effects
+;
+
+include "song_playerconfig.asm"
+include "effects_playerconfig.asm"
+
+include "../../src/mplayer/akm/akm_ubox.asm"
+
+songDisarkGenerateExternalLabel:
+include "song.asm"
+
+effectsDisarkGenerateExternalLabel:
+include "effects.asm"
+```
+
+See the example game and the commands run automatically for further details.
+
+### Exporting the song
+
+In Arkos 2 Tracker, export the song as "AKM", using "Export as source file" and
+"Generate configuration file for players".
+
+Save the file as `song.asm`.
+
+It should create `song_playerconfig.asm` in the same directory.
+
+### Exporting the effects
+
+In Arkos 2 Tracker, export the effects as "AKX", using "Export as source file" and
+"Generate configuration file for players".
+
+Save the file as `effects.asm`.
+
+It should create `effects_playerconfig.asm` in the same directory.
+
diff --git a/docs/spman.md b/docs/spman.md
new file mode 100644
index 0000000..b9512b8
--- /dev/null
+++ b/docs/spman.md
@@ -0,0 +1,36 @@
+## Overview
+
+**spman** is a simple sprite manager that provides:
+
+- support for 16x16 sprites.
+- an easy way to manage sprite patterns.
+- a simple yet effective sprite flicker that allows displaying more than 4 sprites
+ on the same line, with support for "fixed" sprites that are excluded from
+ flickering.
+
+It should be easy to modify and customize (`spman.c` is around 100 lines of code).
+
+The manager is used as follows:
+
+1. Initialize the manager with [spman_init](#spman_init).
+2. Allocate patterns with [spman_alloc](#spman_alloc). This can be done at any
+ time, is not needed to allocate all the patterns in one go.
+3. In the game loop:
+ - Allocate sprites to be drawn on screen, using
+ [spman_alloc_fixed_sprite](#spman_alloc_fixed_sprite) for sprites excluded
+ from the flicker and [spman_alloc_sprite](#spman_alloc_sprite) for any
+ other sprite.
+ - Update the sprites on screen with [spman_update](#spman_update).
+
+The allocate/update cycle needs to happen per frame, so the flicker function is
+effective. The longer between updates, the slower the flickering will be. If
+the game updates at least 25/30 FPS, that's good enough for most games.
+
+There are other functions to flush allocated sprites without updating the
+screen, or to hide all the sprites on screen.
+
+The manager supports all 64 patterns and up to 31 visible sprites (one sprite
+is used by the flicker).
+
+**spman** comes from "Sprite Pattern MANager".
+
diff --git a/docs/water.css b/docs/water.css
new file mode 100644
index 0000000..c007230
--- /dev/null
+++ b/docs/water.css
@@ -0,0 +1,2 @@
+body{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;line-height:1.4;max-width:800px;margin:20px auto;padding:0 10px;color:#dbdbdb;background:#202b38;text-rendering:optimizeLegibility}button,input,textarea{transition:background-color .1s linear,border-color .1s linear,color .1s linear,box-shadow .1s linear,transform .1s ease}h1{font-size:2.2em;margin-top:0}h1,h2,h3,h4,h5,h6{margin-bottom:12px}h1,h2,h3,h4,h5,h6,strong{color:#fff}b,h1,h2,h3,h4,h5,h6,strong,th{font-weight:600}button,input[type=button],input[type=checkbox],input[type=submit]{cursor:pointer}input:not([type=checkbox]),select{display:block}button,input,select,textarea{color:#fff;background-color:#161f27;font-family:inherit;font-size:inherit;margin-right:6px;margin-bottom:6px;padding:10px;border:none;border-radius:6px;outline:none}button,input:not([type=checkbox]),select,textarea{-webkit-appearance:none}textarea{margin-right:0;width:100%;box-sizing:border-box;resize:vertical}button,input[type=button],input[type=submit]{padding-right:30px;padding-left:30px}button:hover,input[type=button]:hover,input[type=submit]:hover{background:#324759}button:focus,input:focus,select:focus,textarea:focus{box-shadow:0 0 0 2px rgba(0,150,191,.67)}button:active,input[type=button]:active,input[type=checkbox]:active,input[type=submit]:active{transform:translateY(2px)}input:disabled{cursor:not-allowed;opacity:.5}::-webkit-input-placeholder{color:#a9a9a9}:-ms-input-placeholder{color:#a9a9a9}::-ms-input-placeholder{color:#a9a9a9}::placeholder{color:#a9a9a9}a{text-decoration:none;color:#41adff}a:hover{text-decoration:underline}code,kbd{background:#161f27;color:#ffbe85;padding:5px;border-radius:6px}pre>code{padding:10px;display:block;overflow-x:auto}img{max-width:100%}hr{border:none;border-top:1px solid #dbdbdb}table{border-collapse:collapse;margin-bottom:10px;width:100%}td,th{padding:6px;text-align:left}th{border-bottom:1px solid #dbdbdb}tbody tr:nth-child(2n){background-color:#161f27}::-webkit-scrollbar{height:10px;width:10px}::-webkit-scrollbar-track{background:#161f27;border-radius:6px}::-webkit-scrollbar-thumb{background:#324759;border-radius:6px}::-webkit-scrollbar-thumb:hover{background:#415c73}
+/*# sourceMappingURL=dark.css.map */
diff --git a/game/Makefile b/game/Makefile
new file mode 100644
index 0000000..da7d699
--- /dev/null
+++ b/game/Makefile
@@ -0,0 +1,12 @@
+all:
+ mkdir -p ./generated ./bin ./build
+ make -C data
+ cd src && rm -f Makefile.deps && touch Makefile.deps && find . -name "*.c" | xargs -n1 sdcc -I../generated -I../../include -MM | sed -r 's/([^:]+):/..\/build\/\1:/' >> Makefile.deps
+ make -C src
+
+.PHONY: all clean
+clean:
+ rm -rf ./generated ./bin ./build
+ make -C src clean
+ make -C data clean
+
diff --git a/game/README.md b/game/README.md
new file mode 100644
index 0000000..36437f2
--- /dev/null
+++ b/game/README.md
@@ -0,0 +1,37 @@
+# GREEN
+
+This is a example game to demo and test functionality of ubox MSX lib.
+
+It has only one screen is not too exciting!
+
+In the menu press fire on any joystick or space to play with cursors.
+
+Use space to activate the elevators and ESC to exit the game.
+
+This game is meant to be a demo, and because of that, it is simple and not
+specially optimised; but you can see that it moves the player and 7 enemies
+just fine and **it is a good example on how to write a game in C for the MSX**.
+
+ GREEN, a demo for ubox MSX lib
+ Copyright (C) 2020 by Juan J. Martinez <jjm@usebox.net>
+
+ 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.
+
+The graphics, music and sound are CC BY NC SA 4.0 (basically: no commercial).
+
diff --git a/game/data/Makefile b/game/data/Makefile
new file mode 100644
index 0000000..f79ae33
--- /dev/null
+++ b/game/data/Makefile
@@ -0,0 +1,21 @@
+OUTPUT=../generated
+GENERATED=$(OUTPUT)/tiles.h $(OUTPUT)/player.h $(OUTPUT)/enemy.h $(OUTPUT)/map.h
+
+all: $(GENERATED)
+
+$(OUTPUT)/tiles.h: tiles.png
+ ../../tools/png2tiles.py -i tiles $< > $@
+
+$(OUTPUT)/player.h: player.png
+ ../../tools/png2sprites.py -i player_sprite $< > $@
+
+$(OUTPUT)/enemy.h: enemy.png
+ ../../tools/png2sprites.py -i enemy_sprite $< > $@
+
+$(OUTPUT)/map.h: map.json map_conf.json
+ ../../tools/map.py --max-ents 11 --room-height 21 map.json map > $@
+
+.PHONY: all clean
+clean:
+ rm -f $(OUTPUT)/*
+
diff --git a/game/data/effects.aks b/game/data/effects.aks
new file mode 100644
index 0000000..9b21571
--- /dev/null
+++ b/game/data/effects.aks
Binary files differ
diff --git a/game/data/enemy.png b/game/data/enemy.png
new file mode 100644
index 0000000..728d9f5
--- /dev/null
+++ b/game/data/enemy.png
Binary files differ
diff --git a/game/data/map.json b/game/data/map.json
new file mode 100644
index 0000000..a5df88c
--- /dev/null
+++ b/game/data/map.json
@@ -0,0 +1,159 @@
+{ "compressionlevel":-1,
+ "editorsettings":
+ {
+ "export":
+ {
+ "target":"."
+ }
+ },
+ "height":21,
+ "infinite":false,
+ "layers":[
+ {
+ "data":[6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 8, 9, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 193, 14, 8, 9, 13, 193, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 225, 14, 8, 9, 13, 225, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 6, 7, 6, 7, 10, 11, 6, 7, 6, 7, 6, 7, 6, 7, 8, 9, 6, 7, 6, 7, 6, 7, 10, 11, 6, 7, 6, 7, 6, 7, 6, 7, 130, 130, 130, 130, 15, 16, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 15, 16, 130, 130, 130, 130, 130, 130, 130, 130, 13, 14, 13, 14, 15, 16, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 15, 16, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 15, 16, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 15, 16, 13, 14, 13, 14, 13, 14, 13, 14, 6, 7, 6, 7, 10, 11, 6, 7, 6, 7, 10, 11, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 10, 11, 6, 7, 6, 7, 6, 7, 6, 7, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 15, 16, 130, 130, 130, 130, 130, 130, 8, 9, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 15, 16, 13, 14, 13, 14, 193, 14, 8, 9, 13, 193, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 15, 16, 13, 14, 13, 14, 225, 14, 8, 9, 13, 225, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 10, 11, 6, 7, 6, 7, 6, 7, 8, 9, 6, 7, 6, 7, 6, 7, 10, 11, 6, 7, 6, 7, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 15, 16, 130, 130, 130, 130, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 15, 16, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 13, 14, 15, 16, 13, 14, 13, 14, 6, 7, 6, 7, 10, 11, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 10, 11, 6, 7, 6, 7, 10, 11, 6, 7, 6, 7, 130, 130, 130, 130, 15, 16, 130, 130, 130, 130, 8, 9, 130, 130, 130, 130, 130, 130, 130, 130, 15, 16, 130, 130, 130, 130, 130, 130, 130, 130, 130, 130, 13, 14, 13, 14, 15, 16, 13, 14, 193, 14, 8, 9, 13, 193, 13, 14, 13, 14, 13, 14, 15, 16, 13, 14, 13, 14, 13, 14, 13, 14, 193, 14, 13, 14, 13, 14, 15, 16, 13, 14, 225, 14, 8, 9, 13, 225, 13, 14, 13, 14, 13, 14, 15, 16, 13, 14, 13, 14, 13, 14, 13, 14, 225, 14, 6, 7, 6, 7, 10, 11, 6, 7, 6, 7, 8, 9, 6, 7, 6, 7, 6, 7, 6, 7, 10, 11, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7],
+ "height":21,
+ "id":1,
+ "name":"Map",
+ "opacity":1,
+ "type":"tilelayer",
+ "visible":true,
+ "width":32,
+ "x":0,
+ "y":0
+ },
+ {
+ "draworder":"topdown",
+ "id":2,
+ "name":"Entities",
+ "objects":[
+ {
+ "height":16,
+ "id":10,
+ "name":"player",
+ "rotation":0,
+ "type":"",
+ "visible":true,
+ "width":16,
+ "x":8,
+ "y":16
+ },
+ {
+ "height":16,
+ "id":11,
+ "name":"enemy",
+ "rotation":0,
+ "type":"",
+ "visible":true,
+ "width":16,
+ "x":168,
+ "y":16
+ },
+ {
+ "height":16,
+ "id":12,
+ "name":"enemy",
+ "rotation":0,
+ "type":"",
+ "visible":true,
+ "width":16,
+ "x":64,
+ "y":48
+ },
+ {
+ "height":16,
+ "id":13,
+ "name":"enemy",
+ "properties":[
+ {
+ "name":"param",
+ "type":"int",
+ "value":1
+ }],
+ "rotation":0,
+ "type":"",
+ "visible":true,
+ "width":16,
+ "x":232,
+ "y":48
+ },
+ {
+ "height":16,
+ "id":14,
+ "name":"enemy",
+ "rotation":0,
+ "type":"",
+ "visible":true,
+ "width":16,
+ "x":104,
+ "y":80
+ },
+ {
+ "height":16,
+ "id":15,
+ "name":"enemy",
+ "rotation":0,
+ "type":"",
+ "visible":true,
+ "width":16,
+ "x":32,
+ "y":112
+ },
+ {
+ "height":16,
+ "id":16,
+ "name":"enemy",
+ "rotation":0,
+ "type":"",
+ "visible":true,
+ "width":16,
+ "x":128,
+ "y":144
+ },
+ {
+ "height":16,
+ "id":18,
+ "name":"enemy",
+ "properties":[
+ {
+ "name":"param",
+ "type":"int",
+ "value":1
+ }],
+ "rotation":0,
+ "type":"",
+ "visible":true,
+ "width":16,
+ "x":144,
+ "y":48
+ }],
+ "opacity":1,
+ "type":"objectgroup",
+ "visible":true,
+ "x":0,
+ "y":0
+ }],
+ "nextlayerid":3,
+ "nextobjectid":19,
+ "orientation":"orthogonal",
+ "renderorder":"right-down",
+ "tiledversion":"1.3.1",
+ "tileheight":8,
+ "tilesets":[
+ {
+ "columns":32,
+ "firstgid":1,
+ "image":"tiles.png",
+ "imageheight":64,
+ "imagewidth":256,
+ "margin":0,
+ "name":"default",
+ "spacing":0,
+ "tilecount":256,
+ "tileheight":8,
+ "tilewidth":8
+ }],
+ "tilewidth":8,
+ "type":"map",
+ "version":1.2,
+ "width":32
+} \ No newline at end of file
diff --git a/game/data/map_conf.json b/game/data/map_conf.json
new file mode 100644
index 0000000..79a7a29
--- /dev/null
+++ b/game/data/map_conf.json
@@ -0,0 +1,19 @@
+{
+ "entities": [
+ {
+ "bytes": 0,
+ "name": "unused",
+ "w": 1
+ },
+ {
+ "bytes": 0,
+ "name": "player",
+ "w": 1
+ },
+ {
+ "bytes": 0,
+ "name": "enemy",
+ "w": 1
+ }
+ ]
+}
diff --git a/game/data/player.png b/game/data/player.png
new file mode 100644
index 0000000..fe6b254
--- /dev/null
+++ b/game/data/player.png
Binary files differ
diff --git a/game/data/song.aks b/game/data/song.aks
new file mode 100644
index 0000000..59b94e1
--- /dev/null
+++ b/game/data/song.aks
Binary files differ
diff --git a/game/data/tiles.png b/game/data/tiles.png
new file mode 100644
index 0000000..82fbb6c
--- /dev/null
+++ b/game/data/tiles.png
Binary files differ
diff --git a/game/src/Makefile b/game/src/Makefile
new file mode 100644
index 0000000..7fa9f2f
--- /dev/null
+++ b/game/src/Makefile
@@ -0,0 +1,48 @@
+TARGET=game
+
+CODE=0x4000
+# leaves 199 bytes for AKM player buffer
+DATA=0xc0de
+
+# HEX, will fill with 0
+ROM_MAX=8000
+
+OUTPUT=../build
+OBJS = $(patsubst %.c,$(OUTPUT)/%.rel,$(wildcard *.c)) $(OUTPUT)/akm.rel
+LIBS = -lubox -lspman -lmplayer
+
+CC=sdcc
+AS=sdasz80
+AR=sdcclib
+CFLAGS=-mz80 --Werror -I../../include -I../generated --fsigned-char --std-sdcc99 --opt-code-speed
+LDFLAGS=-L../../lib -L. --no-std-crt0 --fomit-frame-pointer
+
+all: $(OUTPUT)/$(TARGET).rom
+ @../../tools/chksize 8000 4000 $(OUTPUT)/$(TARGET).map
+ cp ../bin/$(TARGET).rom ../../bin
+
+openmsx: all
+ openmsx -carta $(OUTPUT)/$(TARGET).rom -machine msx1
+
+$(OUTPUT)/%.rel: %.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -c $< -o $@
+
+$(OUTPUT)/%.rel: %.z80
+ $(AS) -g -o $@ $<
+
+$(OUTPUT)/akm.rel: akm.z80 song.asm effects.asm
+ ../../bin/rasm akm.z80 -o $(OUTPUT)/akm -s -sl -sq
+ Disark --sourceProfile sdcc --symbolFile $(OUTPUT)/akm.sym --src16bitsValuesInHex --src8bitsValuesInHex --undocumentedOpcodesToBytes $(OUTPUT)/akm.bin $(OUTPUT)/akm_sdcc.asm
+ $(AS) -g -o $@ $(OUTPUT)/akm_sdcc.asm
+
+$(OUTPUT)/$(TARGET).rom: $(OBJS) $(OUTPUT)/crt0.rel ../../lib/*.lib
+ $(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) --code-loc $(CODE) --data-loc $(DATA) $(OUTPUT)/crt0.rel $(OBJS) -o $(OUTPUT)/$(TARGET).ihx
+ ../../bin/hex2bin -e bin -p 00 -l $(ROM_MAX) $(OUTPUT)/$(TARGET).ihx
+ mv $(OUTPUT)/$(TARGET).bin ../bin/$(TARGET).rom
+
+clean:
+ rm -f $(OUTPUT)/*
+
+.PHONY: all clean
+
+include Makefile.deps
diff --git a/game/src/akm.z80 b/game/src/akm.z80
new file mode 100644
index 0000000..97c8498
--- /dev/null
+++ b/game/src/akm.z80
@@ -0,0 +1,15 @@
+;
+; to build the custom AKM player with song + effects
+;
+
+include "song_playerconfig.asm"
+include "effects_playerconfig.asm"
+
+include "../../src/mplayer/akm/akm_ubox.asm"
+
+songDisarkGenerateExternalLabel:
+include "song.asm"
+
+effectsDisarkGenerateExternalLabel:
+include "effects.asm"
+
diff --git a/game/src/aux.c b/game/src/aux.c
new file mode 100644
index 0000000..15fc2a4
--- /dev/null
+++ b/game/src/aux.c
@@ -0,0 +1,27 @@
+#include <stdint.h>
+
+#include "ubox.h"
+
+#include "aux.h"
+
+/**
+ * Put a zero terminated string on the screen using tiles.
+ *
+ * The font starts on the tileset on tile 128 and the fist char in our has
+ * ASCII value 31, so it is adjusted so we can use ASCII *uppercase* directly
+ * in our C code.
+ */
+void put_text(uint8_t x, uint8_t y, const uint8_t *text)
+{
+ while (*text)
+ ubox_put_tile(x++, y, *text++ + 128 - 31);
+}
+
+/**
+ * Wait for `frames` frames.
+ */
+void wait_for(uint8_t frames)
+{
+ while (frames--)
+ ubox_wait();
+}
diff --git a/game/src/aux.h b/game/src/aux.h
new file mode 100644
index 0000000..bf62d1a
--- /dev/null
+++ b/game/src/aux.h
@@ -0,0 +1,13 @@
+#ifndef _AUX_H
+#define _AUX_H
+
+#include <stdint.h>
+
+/**
+ * Auxiliary functions.
+ */
+
+void put_text(uint8_t x, uint8_t y, const uint8_t *text);
+void wait_for(uint8_t frames);
+
+#endif // _AUX_H
diff --git a/game/src/crt0.z80 b/game/src/crt0.z80
new file mode 100644
index 0000000..81763d0
--- /dev/null
+++ b/game/src/crt0.z80
@@ -0,0 +1,82 @@
+.module crt0
+.globl _main
+
+.area _HOME
+.area _CODE
+.area _INITIALIZER
+.area _GSINIT
+.area _GSFINAL
+
+.area _DATA
+.area _INITIALIZED
+.area _BSEG
+.area _BSS
+.area _HEAP
+
+.area _CODE
+
+ENASLT = 0x0024
+RSLREG = 0x0138
+CLIKSW = 0xf3db
+
+ ; ROM header
+ .str "AB"
+ .dw _main_init
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+
+_main_init::
+
+ ; init the stack
+ di
+ ld sp, #0xf380
+ ei
+
+ ; setup memory
+ ; ref: https://www.msx.org/forum/msx-talk/development/memory-pages-again
+
+ call RSLREG
+ rrca
+ rrca
+ and #3
+ ld c, a
+ add a, #0xc1
+ ld l, a
+ ld h, #0xfc
+ ld a, (hl)
+ and #0x80
+ or c
+ ld c, a
+ inc l
+ inc l
+ inc l
+ inc l
+ ld a, (hl)
+ and #0x0c
+ or c
+ ld h, #0x80
+ call ENASLT
+
+ ; disable key click sound
+ xor a
+ ld (CLIKSW), a
+
+ call gsinit
+ call _main
+
+halt0:
+ halt
+ jr halt0
+
+.area _GSINIT
+gsinit::
+ ld bc, #l__INITIALIZER
+ ld a, b
+ or a, c
+ jr Z, gsinit_next
+ ld de, #s__INITIALIZED
+ ld hl, #s__INITIALIZER
+ ldir
+gsinit_next:
+
+.area _GSFINAL
+ ret
diff --git a/game/src/data.c b/game/src/data.c
new file mode 100644
index 0000000..67bec4e
--- /dev/null
+++ b/game/src/data.c
@@ -0,0 +1,9 @@
+/**
+ * Module storing the generated data.
+ */
+
+#define LOCAL
+#include "tiles.h"
+#include "player.h"
+#include "enemy.h"
+#include "map.h"
diff --git a/game/src/effects.asm b/game/src/effects.asm
new file mode 100644
index 0000000..0ff8465
--- /dev/null
+++ b/game/src/effects.asm
@@ -0,0 +1,249 @@
+; Sound Effects for song Green, version 2.0, generated by Arkos Tracker 2.
+
+Green_SoundEffects
+Green_SoundEffectsDisarkGenerateExternalLabel
+
+; The sound effects, starting at 1.
+Green_SoundEffectsDisarkPointerRegionStart0
+ dw Green_SoundEffects_Sound1 ; Sound effect 1.
+ dw Green_SoundEffects_Sound2 ; Sound effect 2.
+ dw Green_SoundEffects_Sound3 ; Sound effect 3.
+ dw Green_SoundEffects_Sound4 ; Sound effect 4.
+ dw Green_SoundEffects_Sound5 ; Sound effect 5.
+Green_SoundEffectsDisarkPointerRegionEnd0
+
+Green_SoundEffectsDisarkByteRegionStart1
+; Sound effect 1.
+Green_SoundEffects_Sound1
+ db 0 ; Speed
+
+Green_SoundEffects_Sound1_Loop db 57 ; Soft only. Volume: 14.
+ dw 96 ; Software period.
+
+ db 57 ; Soft only. Volume: 14.
+ dw 96 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 128 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 128 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 256 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 256 ; Software period.
+
+ db 37 ; Soft only. Volume: 9.
+ dw 128 ; Software period.
+
+ db 37 ; Soft only. Volume: 9.
+ dw 128 ; Software period.
+
+ db 29 ; Soft only. Volume: 7.
+ dw 128 ; Software period.
+
+ db 29 ; Soft only. Volume: 7.
+ dw 128 ; Software period.
+
+ db 4 ; End of the sound effect.
+
+; Sound effect 2.
+Green_SoundEffects_Sound2
+ db 0 ; Speed
+
+ db 57 ; Soft only. Volume: 14.
+ dw 96 ; Software period.
+
+ db 57 ; Soft only. Volume: 14.
+ dw 96 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 112 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 112 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 128 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 128 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 128 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 128 ; Software period.
+
+ db 1 ; Soft only. Volume: 0.
+ dw 128 ; Software period.
+
+ db 1 ; Soft only. Volume: 0.
+ dw 128 ; Software period.
+
+ db 33 ; Soft only. Volume: 8.
+ dw 96 ; Software period.
+
+ db 33 ; Soft only. Volume: 8.
+ dw 96 ; Software period.
+
+Green_SoundEffects_Sound2_Loop db 1 ; Soft only. Volume: 0.
+ dw 96 ; Software period.
+
+ db 1 ; Soft only. Volume: 0.
+ dw 96 ; Software period.
+
+ db 25 ; Soft only. Volume: 6.
+ dw 128 ; Software period.
+
+ db 25 ; Soft only. Volume: 6.
+ dw 128 ; Software period.
+
+ db 4 ; End of the sound effect.
+
+; Sound effect 3.
+Green_SoundEffects_Sound3
+ db 0 ; Speed
+
+ db 177 ; Soft only. Volume: 12.
+ db 1 ; Noise: 1.
+ dw 128 ; Software period.
+
+ db 177 ; Soft only. Volume: 12.
+ db 1 ; Noise: 1.
+ dw 128 ; Software period.
+
+ db 177 ; Soft only. Volume: 12.
+ db 1 ; Noise: 1.
+ dw 128 ; Software period.
+
+ db 177 ; Soft only. Volume: 12.
+ db 1 ; Noise: 1.
+ dw 128 ; Software period.
+
+ db 49 ; Soft only. Volume: 12.
+ dw 128 ; Software period.
+
+ db 49 ; Soft only. Volume: 12.
+ dw 96 ; Software period.
+
+ db 49 ; Soft only. Volume: 12.
+ dw 96 ; Software period.
+
+ db 49 ; Soft only. Volume: 12.
+ dw 256 ; Software period.
+
+Green_SoundEffects_Sound3_Loop db 49 ; Soft only. Volume: 12.
+ dw 256 ; Software period.
+
+ db 4 ; End of the sound effect.
+
+; Sound effect 4.
+Green_SoundEffects_Sound4
+ db 0 ; Speed
+
+ db 185 ; Soft only. Volume: 14.
+ db 1 ; Noise: 1.
+ dw 256 ; Software period.
+
+ db 185 ; Soft only. Volume: 14.
+ db 1 ; Noise: 1.
+ dw 256 ; Software period.
+
+ db 185 ; Soft only. Volume: 14.
+ db 1 ; Noise: 1.
+ dw 512 ; Software period.
+
+ db 185 ; Soft only. Volume: 14.
+ db 1 ; Noise: 1.
+ dw 512 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 640 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 640 ; Software period.
+
+ db 45 ; Soft only. Volume: 11.
+ dw 608 ; Software period.
+
+ db 45 ; Soft only. Volume: 11.
+ dw 608 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 768 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 768 ; Software period.
+
+ db 33 ; Soft only. Volume: 8.
+ dw 1024 ; Software period.
+
+Green_SoundEffects_Sound4_Loop db 33 ; Soft only. Volume: 8.
+ dw 1024 ; Software period.
+
+ db 4 ; End of the sound effect.
+
+; Sound effect 5.
+Green_SoundEffects_Sound5
+ db 0 ; Speed
+
+ db 57 ; Soft only. Volume: 14.
+ dw 512 ; Software period.
+
+ db 57 ; Soft only. Volume: 14.
+ dw 512 ; Software period.
+
+ db 57 ; Soft only. Volume: 14.
+ dw 608 ; Software period.
+
+ db 57 ; Soft only. Volume: 14.
+ dw 608 ; Software period.
+
+ db 57 ; Soft only. Volume: 14.
+ dw 800 ; Software period.
+
+ db 57 ; Soft only. Volume: 14.
+ dw 800 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 768 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 1024 ; Software period.
+
+ db 45 ; Soft only. Volume: 11.
+ dw 1280 ; Software period.
+
+ db 45 ; Soft only. Volume: 11.
+ dw 1536 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 2048 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 2048 ; Software period.
+
+ db 33 ; Soft only. Volume: 8.
+ dw 2048 ; Software period.
+
+ db 33 ; Soft only. Volume: 8.
+ dw 2048 ; Software period.
+
+ db 33 ; Soft only. Volume: 8.
+ dw 2048 ; Software period.
+
+ db 33 ; Soft only. Volume: 8.
+ dw 2048 ; Software period.
+
+ db 25 ; Soft only. Volume: 6.
+ dw 2048 ; Software period.
+
+Green_SoundEffects_Sound5_Loop db 25 ; Soft only. Volume: 6.
+ dw 2048 ; Software period.
+
+ db 4 ; End of the sound effect.
+
+Green_SoundEffectsDisarkByteRegionEnd1
diff --git a/game/src/effects_playerconfig.asm b/game/src/effects_playerconfig.asm
new file mode 100644
index 0000000..f9d189c
--- /dev/null
+++ b/game/src/effects_playerconfig.asm
@@ -0,0 +1,14 @@
+; Configuration that can be included to Arkos Tracker 2 players.
+; It indicates what parts of code are useful to the song/sound effects, to save both memory and CPU.
+; The players may or may not take advantage of these flags, it is up to them.
+
+; You can either:
+; - Include this to the source that also includes the player (BEFORE the player is included) (recommended solution).
+; - Include this at the beginning of the player code.
+; - Copy/paste this directly in the player.
+; If you use one player but several songs, don't worry, these declarations will stack up.
+; If no configuration is used, the player will use default values (full code used).
+
+ PLY_CFG_SFX_ConfigurationIsPresent = 1
+ PLY_CFG_SFX_SoftOnly = 1
+ PLY_CFG_SFX_SoftOnly_Noise = 1
diff --git a/game/src/game.c b/game/src/game.c
new file mode 100644
index 0000000..bc52b2a
--- /dev/null
+++ b/game/src/game.c
@@ -0,0 +1,437 @@
+#include <stdint.h>
+#include <string.h>
+
+#include "ubox.h"
+#include "spman.h"
+#include "mplayer.h"
+
+#include "aux.h"
+#include "main.h"
+
+#define LOCAL
+#include "game.h"
+
+// generated
+#include "map.h"
+#include "player.h"
+#include "enemy.h"
+
+void init_map_entities()
+{
+ const uint8_t *m = cur_map;
+ uint8_t typ, last = 0;
+ uint16_t i;
+
+ // init sprite and patterns
+ spman_init();
+
+ // this sets everything to 0, which is useful as
+ // entity ET_UNUSED is 0
+ memset(entities, 0, sizeof(struct entity) * MAX_ENTITIES);
+
+ // get to the beginning of the entities:
+ // map size + 3 bytes of header (the map size and the entities size)
+ m += (uint16_t)(m[0] | m[1] << 8) + 3;
+
+ // the entity list ends with 255
+ while (*m != 0xff)
+ {
+ // first byte is type + direction flag on MSB
+ // remove MSB
+ typ = m[0] & (~DIR_FLAG);
+
+ entities[last].type = typ;
+ entities[last].x = m[1];
+ entities[last].y = m[2];
+ // in the map: param is 1 (int) to look left
+ entities[last].dir = m[0] & DIR_FLAG ? 1 : 0;
+
+ switch (typ)
+ {
+ // can be only one; always first entity
+ // because our entities are sorted by type!
+ case ET_PLAYER:
+ // 3 frames x 2 sprites = 6
+ entities[last].pat = spman_alloc_pat(PAT_PLAYER, player_sprite[0], 6, 0);
+ spman_alloc_pat(PAT_PLAYER_FLIP, player_sprite[0], 6, 1);
+ entities[last].update = update_player;
+ break;
+
+ case ET_ENEMY:
+ // 3 frames
+ entities[last].pat = spman_alloc_pat(PAT_ENEMY, enemy_sprite[0], 3, 0);
+ spman_alloc_pat(PAT_ENEMY_FLIP, enemy_sprite[0], 3, 1);
+ entities[last].update = update_enemy;
+ break;
+ }
+
+ // next entity
+ last++;
+
+ // all our entities are 3 bytes
+ m += 3;
+ }
+
+ // count how many batteries are in the map
+ batteries = 0;
+ for (i = 0; i < MAP_W * MAP_H; ++i)
+ if (cur_map_data[i] == BATTERY_TILE)
+ batteries++;
+
+}
+
+void draw_map()
+{
+ // draw the map!
+ //
+ // - is not compressed (which limits how many maps we can store)
+ // - our map is just the tile numbers
+ //
+ // so we do it in one call by coping our map to the backtround tile map
+ // addr in the VDP VRAM
+ ubox_wait_vsync();
+ ubox_write_vm((uint8_t *)0x1800, MAP_W * MAP_H, cur_map_data);
+}
+
+void draw_hud()
+{
+ uint8_t i;
+
+ put_text(0, 21, "LIVES");
+
+ for (i = 0; i < MAX_LIVES; ++i)
+ if (i < lives)
+ // our hearts tile
+ ubox_put_tile(1 + i, 22, 193);
+ else
+ ubox_put_tile(1 + i, 22, WHITESPACE_TILE);
+}
+
+// x and y in pixels
+void erase_battery(uint8_t x, uint8_t y)
+{
+ uint8_t t;
+ int8_t mod;
+
+ // find out the bg tile to use
+
+ // border of the map
+ if ((x >> 3) == 0)
+ mod = 1;
+ else
+ mod = -1;
+
+ switch (cur_map_data[mod + (x >> 3) + (y >> 3) * MAP_W])
+ {
+ case 12:
+ t = 13;
+ break;
+ case 13:
+ t = 12;
+ break;
+ default:
+ // this is next to a wall
+ if (mod == 1)
+ t = 13;
+ else
+ t = 12;
+ break;
+ }
+
+ // change the map data so we don't pick it up again
+ cur_map_data[(x >> 3) + (y >> 3) * MAP_W] = t;
+
+ // erase on the screen
+ ubox_put_tile(x >> 3, y >> 3, t);
+ ubox_put_tile(x >> 3, (y >> 3) - 1, t);
+}
+
+// x and y in pixels
+uint8_t is_map_blocked(uint8_t x, uint8_t y)
+{
+ return cur_map_data[(x >> 3) + (y >> 3) * MAP_W] < LAST_SOLID_TILE + 1;
+}
+
+// x and y in pixels
+uint8_t is_map_battery(uint8_t x, uint8_t y)
+{
+ return cur_map_data[(x >> 3) + (y >> 3) * MAP_W] == BATTERY_TILE;
+}
+
+// x and y in pixels; always check the bottom tile!
+uint8_t is_map_elevator_down(uint8_t x, uint8_t y)
+{
+ uint8_t t = cur_map_data[(x >> 3) + (y >> 3) * MAP_W];
+
+ // first check the elevator platform comparing tiles
+ if (t != 9 && t != 10)
+ return 0;
+
+ // then check the elevator tube comparing tiles
+ t = cur_map_data[(x >> 3) + ((y >> 3) - 1) * MAP_W];
+ return (t != 14 && t != 15);
+}
+
+// x and y in pixels; always check the bottom tile!
+uint8_t is_map_elevator_up(uint8_t x, uint8_t y)
+{
+ uint8_t t = cur_map_data[(x >> 3) + (y >> 3) * MAP_W];
+
+ // first check the elevator platform comparing tiles
+ if (t != 9 && t != 10)
+ return 0;
+
+ // then check the elevator tube comparing tiles
+ t = cur_map_data[(x >> 3) + ((y >> 3) - 1) * MAP_W];
+ return (t == 14 || t == 15);
+}
+
+void update_enemy()
+{
+ // check for the player; if alive and not invulnerable!
+ // we use small hit boxes
+ if (lives && !invuln
+ && entities[0].x + 6 < self->x + 10 && self->x + 6 < entities[0].x + 10
+ && self->y == entities[0].y)
+ {
+ // change direction
+ self->dir ^= 1;
+
+ // remove one life (is more like "hits")
+ lives--;
+ draw_hud();
+ invuln = INVUL_TIME;
+
+ if (!lives)
+ {
+ // different sound effects if is game over
+ mplayer_init(SONG, SONG_SILENCE);
+ mplayer_play_effect_p(EFX_DEAD, EFX_CHAN_NO, 0);
+ gameover_delay = GAMEOVER_DELAY;
+ }
+ else
+ mplayer_play_effect_p(EFX_HIT, EFX_CHAN_NO, 0);
+ }
+
+ // left or right?
+ if (self->dir)
+ {
+ // change direction
+ if (self->x == 2 || is_map_blocked(self->x, self->y + 15))
+ self->dir ^= 1;
+ else
+ self->x -= 1;
+ }
+ else
+ {
+ // change direction
+ if (self->x == 255 - 16 || is_map_blocked(self->x + 15, self->y + 15))
+ self->dir ^= 1;
+ else
+ self->x += 1;
+ }
+
+ // update the walking animation
+ if (self->delay++ == FRAME_WAIT)
+ {
+ self->delay = 0;
+ if (++self->frame == WALK_CYCLE)
+ self->frame = 0;
+ }
+
+ // allocate the sprites
+ sp.x = self->x;
+ // y on the screen starts in 255
+ sp.y = self->y - 1;
+ // find which pattern to show
+ sp.pattern = self->pat + (walk_frames[self->frame] + self->dir * 3) * 4;
+ // red
+ sp.attr = 9;
+ spman_alloc_sprite(&sp);
+}
+
+void update_player()
+{
+ // to know if we need to update the walk animation
+ uint8_t moved = 0;
+
+ // player is dead
+ if (!lives)
+ return;
+
+ // decrease counter if set
+ if (invuln)
+ invuln--;
+
+ if (control & UBOX_MSX_CTL_RIGHT)
+ {
+ self->dir = DIR_RIGHT;
+ moved = 1;
+
+ // wrap horizontally
+ if (self->x == 255 - 16)
+ self->x = 0;
+ // check if not solid, using bottom right
+ else if (!is_map_blocked(self->x + 15, self->y + 15))
+ self->x += 2;
+ }
+
+ if (control & UBOX_MSX_CTL_LEFT)
+ {
+ self->dir = DIR_LEFT;
+ moved = 1;
+
+ // wrap horizontally
+ if (self->x == 2)
+ self->x = (uint8_t)(255 - 16);
+ // check if not solid, using bottom left
+ else if (!is_map_blocked(self->x, self->y + 15))
+ self->x -= 2;
+ }
+
+ // are we touching a battery?
+ // use bottom center
+ if (is_map_battery(self->x + 8, self->y + 15))
+ {
+ mplayer_play_effect_p(EFX_BATTERY, EFX_CHAN_NO, 0);
+ batteries--;
+ erase_battery(self->x + 8, self->y + 15);
+ }
+
+ if (control & UBOX_MSX_CTL_FIRE1)
+ {
+ // use flags to prevent repeat: the player will
+ // have to release fire to use the elevator again
+ if (!self->flags)
+ {
+ self->flags = 1;
+
+ // check elevator down; using bottom middle
+ if (is_map_elevator_down(self->x + 8, self->y + 16))
+ {
+ mplayer_play_effect_p(EFX_ELEVATOR, EFX_CHAN_NO, 0);
+ self->y += 8 * 4;
+ }
+ // then elevator up; using bottom middle
+ else if (is_map_elevator_up(self->x + 8, self->y + 16))
+ {
+ mplayer_play_effect_p(EFX_ELEVATOR, EFX_CHAN_NO, 0);
+ self->y -= 8 * 4;
+ }
+ }
+ }
+ else
+ {
+ if (self->flags)
+ self->flags = 0;
+ }
+
+ // it moved, or at least pushed against a wall
+ if (moved)
+ {
+ // update the walking animation
+ if (self->delay++ == FRAME_WAIT)
+ {
+ self->delay = 0;
+ if (++self->frame == WALK_CYCLE)
+ self->frame = 0;
+ }
+ }
+ else
+ {
+ // just stand
+ if (self->frame)
+ {
+ self->frame = 0;
+ self->delay = 0;
+ }
+ }
+
+ // if we are invulnerable, don't draw odd frames
+ // and we get a nice blinking effect
+ if (invuln & 1)
+ return;
+
+ // allocate the player sprites; fixed so they never flicker
+ sp.x = self->x;
+ // y on the screen starts in 255
+ sp.y = self->y - 1;
+ // find which pattern to show
+ sp.pattern = self->pat + (walk_frames[self->frame] + self->dir * 3) * 8;
+ // green
+ sp.attr = 12;
+ spman_alloc_fixed_sprite(&sp);
+ // second one is 4 patterns away (16x16 sprites)
+ sp.pattern += 4;
+ // white
+ sp.attr = 15;
+ spman_alloc_fixed_sprite(&sp);
+}
+
+void run_game()
+{
+ // init some variables; look at game.h for description
+ lives = MAX_LIVES;
+ invuln = 0;
+ gameover_delay = 0;
+
+ ubox_disable_screen();
+
+ ubox_fill_screen(WHITESPACE_TILE);
+
+ // we only have one map, select it
+ cur_map = map[0];
+ // copy map data into RAM, we will modify it
+ // map data starts on byte 3 (skip map data size and entities size)
+ memcpy(cur_map_data, cur_map + 3, MAP_W * MAP_H);
+
+ // init entities before drawing
+ init_map_entities();
+ draw_map();
+
+ draw_hud();
+
+ ubox_enable_screen();
+
+ mplayer_init(SONG, SONG_IN_GAME);
+
+ // our game loop
+ while (1)
+ {
+ // exit the game
+ if (ubox_read_keys(7) == UBOX_MSX_KEY_ESC)
+ break;
+
+ // game completed!
+ if (!batteries)
+ break;
+
+ // we are in the gameover delay
+ if (gameover_delay)
+ {
+ // if finished, exit
+ if (--gameover_delay == 0)
+ break;
+ }
+
+ // read the selected control
+ control = ubox_read_ctl(ctl);
+
+ // update all the entities:
+ // - self is a pointer to THIS entity
+ // - because we don't create/destroy entities dynamically
+ // when we found one that is unused we are done
+ for (self = entities; self->type; self++)
+ self->update();
+
+ // ensure we wait to our desired update rate
+ ubox_wait();
+ // update sprites on screen
+ spman_update();
+ }
+
+ // stop the in game music
+ mplayer_init(SONG, SONG_IN_GAME);
+ // hide all the sprites before going back to the menu
+ spman_hide_all_sprites();
+}
diff --git a/game/src/game.h b/game/src/game.h
new file mode 100644
index 0000000..9227987
--- /dev/null
+++ b/game/src/game.h
@@ -0,0 +1,130 @@
+#ifndef _GAME_H
+#define _GAME_H
+
+#ifndef LOCAL
+#define LOCAL extern
+#else
+
+#define WALK_CYCLE 4
+
+// walk animation frames, private
+const uint8_t walk_frames[WALK_CYCLE] = { 0, 1, 0, 2 };
+
+#endif
+
+/**
+ * Game implementation and helpers.
+ */
+
+// map size in tiles
+#define MAP_W 32
+#define MAP_H 21
+
+// some useful tiles
+#define LAST_SOLID_TILE 10
+#define BATTERY_TILE 224
+
+#define MAX_LIVES 3
+
+// player + other entities
+#define MAX_ENTITIES 11
+
+// MSB in our entity data
+#define DIR_FLAG 128
+
+// sprite direction
+#define DIR_LEFT 1
+#define DIR_RIGHT 0
+
+// how long we wait between animation frames
+#define FRAME_WAIT 3
+
+// how long the player is invulnerable after death
+// time in frames
+#define INVUL_TIME 64
+
+// show some game frames before jumping to the game over
+// screen; this helps the player to know what happened
+#define GAMEOVER_DELAY 72
+
+// types for our pattern groups
+// used by spman
+enum pattern_type
+{
+ PAT_PLAYER = 0,
+ PAT_PLAYER_FLIP,
+ PAT_ENEMY,
+ PAT_ENEMY_FLIP,
+};
+
+// entity types in the same order
+// used in the map (see map_conf.json)
+enum entity_type
+{
+ ET_UNUSED = 0,
+ ET_PLAYER,
+ ET_ENEMY,
+};
+
+// notes:
+//
+// - x, y are the logic position on the map
+// - sp.x, sp.y are the position on screen
+struct entity
+{
+ uint8_t type;
+ uint8_t x;
+ uint8_t y;
+ uint8_t dir;
+ uint8_t pat;
+ uint8_t flags;
+ uint8_t delay;
+ uint8_t frame;
+ void (*update)();
+};
+
+void run_game();
+
+void update_player();
+void update_enemy();
+
+void draw_map();
+void draw_hud();
+
+void erase_battery(uint8_t x, uint8_t y);
+
+uint8_t is_map_blocked(uint8_t x, uint8_t y);
+uint8_t is_map_elevator_up(uint8_t x, uint8_t y);
+uint8_t is_map_elevator_down(uint8_t x, uint8_t y);
+
+// our entities; the player is 0
+LOCAL struct entity entities[MAX_ENTITIES];
+
+// used to read our control method
+LOCAL uint8_t control;
+
+// current map; we don't use ROM because we will modify it
+LOCAL const uint8_t *cur_map;
+// current map tile map (map data, not entities)
+LOCAL uint8_t cur_map_data[MAP_W * MAP_H];
+
+// player lives
+LOCAL uint8_t lives;
+// invulnerability time after death
+LOCAL uint8_t invuln;
+// batteries left
+LOCAL uint8_t batteries;
+// show dome delay once the lives run out
+LOCAL uint8_t gameover_delay;
+
+// used by all entities
+LOCAL struct sprite_attr sp;
+
+// current entity
+LOCAL struct entity *self;
+
+#ifdef LOCAL
+#undef LOCAL
+#endif
+
+#endif // _GAME_H
diff --git a/game/src/main.c b/game/src/main.c
new file mode 100644
index 0000000..1405356
--- /dev/null
+++ b/game/src/main.c
@@ -0,0 +1,143 @@
+#include <stdint.h>
+
+#include "ubox.h"
+#include "mplayer.h"
+
+#include "aux.h"
+#include "game.h"
+
+#define LOCAL
+#include "main.h"
+
+// generated
+#include "tiles.h"
+
+void draw_menu()
+{
+ uint8_t i;
+
+ ubox_disable_screen();
+
+ ubox_fill_screen(WHITESPACE_TILE);
+
+ // game logo; starts in tile 32 and it is 10 x 3 tiles
+ for (i = 0; i < 10; ++i)
+ {
+ ubox_put_tile(11 + i, 6, 32 + i);
+ ubox_put_tile(11 + i, 7, 64 + i);
+ ubox_put_tile(11 + i, 8, 96 + i);
+ }
+
+ put_text(11, 11, "PRESS FIRE");
+
+ put_text(7, 2, "UBOX MSX LIB DEMO!");
+ put_text(4, 16, "CODE, GRAPHICS AND SOUND");
+ put_text(8, 17, "JUAN J. MARTINEZ");
+ // 037 is ASCII 31 in octal, our Copyright sign
+ put_text(8, 21, "\0372020 USEBOX.NET");
+
+ ubox_enable_screen();
+}
+
+void draw_end_game()
+{
+ ubox_disable_screen();
+
+ ubox_fill_screen(WHITESPACE_TILE);
+
+ put_text(3, 9, "WELL DONE!");
+ put_text(3, 10, "YOU HAVE FINISHED THE DEMO.");
+ put_text(3, 12, "NOW GO AND MAKE GAMES :)");
+
+ put_text(3, 14, "(PRESS ESC)");
+
+ ubox_enable_screen();
+
+ // wait until ESC is pressed
+ while (1)
+ {
+ if (ubox_read_keys(7) == UBOX_MSX_KEY_ESC)
+ break;
+
+ ubox_wait();
+ }
+}
+
+void draw_game_over()
+{
+ ubox_disable_screen();
+
+ ubox_fill_screen(WHITESPACE_TILE);
+ put_text(11, 10, "GAME OVER");
+
+ ubox_enable_screen();
+
+ // play game over music
+ mplayer_init(SONG, SONG_GAME_OVER);
+
+ wait_for(128);
+}
+
+void main()
+{
+ // PAL: 50/2 = 25 FPS
+ // NTSC: 60/2 = 30 FPS
+ ubox_init_isr(2);
+
+ // set screen 2
+ ubox_set_mode(2);
+ // all black
+ ubox_set_colors(1, 1, 1);
+
+ // avoid showing garbage on the screen
+ // while setting up the tiles
+ ubox_disable_screen();
+
+ // upload our tileset
+ ubox_set_tiles(tiles);
+ // and the colour information
+ ubox_set_tiles_colors(tiles_colors);
+
+ // clear the screen
+ ubox_fill_screen(WHITESPACE_TILE);
+
+ ubox_enable_screen();
+
+ // reg 1: activate sprites, v-blank int on, 16x16 sprites
+ ubox_wvdp(1, 0xe2);
+
+ // init the player
+ mplayer_init(SONG, SONG_SILENCE);
+ mplayer_init_effects(EFFECTS);
+
+ // we don't need anything in our ISR
+ // other than the play function, so we
+ // use that!
+ ubox_set_user_isr(mplayer_play);
+
+redraw_menu:
+ draw_menu();
+
+ // whait until fire is pressed
+ // can be space (control will be cursors), or any fire button on a joystick
+ while (1)
+ {
+ ctl = ubox_select_ctl();
+ if (ctl != UBOX_MSX_CTL_NONE)
+ {
+ mplayer_play_effect_p(EFX_START, EFX_CHAN_NO, 0);
+ wait_for(16);
+
+ // play the game
+ run_game();
+
+ if (!lives)
+ draw_game_over();
+ else if (!batteries)
+ draw_end_game();
+
+ goto redraw_menu;
+ }
+ ubox_wait();
+ }
+}
diff --git a/game/src/main.h b/game/src/main.h
new file mode 100644
index 0000000..f8d5d77
--- /dev/null
+++ b/game/src/main.h
@@ -0,0 +1,50 @@
+#ifndef _MAIN_H
+#define _MAIN_H
+
+#include <stdint.h>
+
+#ifndef LOCAL
+#define LOCAL extern
+#endif
+
+#define WHITESPACE_TILE 129
+
+// sub-songs matching our Arkos song
+// configure the song to use MSX AY
+enum songs
+{
+ SONG_SILENCE = 0,
+ SONG_IN_GAME,
+ SONG_GAME_OVER,
+};
+
+// we will use 0 and 1 for the music
+#define EFX_CHAN_NO 2
+
+// sound effects matching our Arkos efc song
+// configure the song to use MSX AY
+enum effects
+{
+ EFX_NONE = 0,
+ EFX_START,
+ EFX_BATTERY,
+ EFX_ELEVATOR,
+ EFX_HIT,
+ EFX_DEAD,
+};
+
+void draw_end_game();
+void draw_menu();
+
+// store the selected control
+LOCAL uint8_t ctl;
+
+// Arkos data
+extern uint8_t SONG[];
+extern uint8_t EFFECTS[];
+
+#ifdef LOCAL
+#undef LOCAL
+#endif
+
+#endif // _MAIN_H
diff --git a/game/src/song.asm b/game/src/song.asm
new file mode 100644
index 0000000..de3ca50
--- /dev/null
+++ b/game/src/song.asm
@@ -0,0 +1,650 @@
+; Green, Song part, encoded in the AKM (minimalist) format V0.
+
+
+Green_Start
+Green_StartDisarkGenerateExternalLabel
+
+Green_DisarkPointerRegionStart0
+ dw Green_InstrumentIndexes ; Index table for the Instruments.
+Green_DisarkForceNonReferenceDuring2_1
+ dw 0 ; Index table for the Arpeggios.
+Green_DisarkForceNonReferenceDuring2_2
+ dw 0 ; Index table for the Pitches.
+
+; The subsongs references.
+ dw Green_Subsong0
+ dw Green_Subsong1
+ dw Green_Subsong2
+Green_DisarkPointerRegionEnd0
+
+; The Instrument indexes.
+Green_InstrumentIndexes
+Green_DisarkPointerRegionStart3
+ dw Green_Instrument0
+ dw Green_Instrument1
+ dw Green_Instrument2
+ dw Green_Instrument3
+ dw Green_Instrument4
+Green_DisarkPointerRegionEnd3
+
+; The Instrument.
+Green_DisarkByteRegionStart4
+Green_Instrument0
+ db 255 ; Speed.
+
+Green_Instrument0Loop db 0 ; Volume: 0.
+
+ db 4 ; End the instrument.
+Green_DisarkPointerRegionStart5
+ dw Green_Instrument0Loop ; Loops.
+Green_DisarkPointerRegionEnd5
+
+Green_Instrument1
+ db 0 ; Speed.
+
+ db 173 ; Volume: 11.
+ db 232 ; Arpeggio: -12.
+
+ db 45 ; Volume: 11.
+
+ db 45 ; Volume: 11.
+
+ db 45 ; Volume: 11.
+
+ db 45 ; Volume: 11.
+
+ db 4 ; End the instrument.
+Green_DisarkPointerRegionStart6
+ dw Green_Instrument0Loop ; Loop to silence.
+Green_DisarkPointerRegionEnd6
+
+Green_Instrument2
+ db 0 ; Speed.
+
+ db 173 ; Volume: 11.
+ db 232 ; Arpeggio: -12.
+
+ db 45 ; Volume: 11.
+
+ db 173 ; Volume: 11.
+ db 232 ; Arpeggio: -12.
+
+ db 45 ; Volume: 11.
+
+ db 4 ; End the instrument.
+Green_DisarkPointerRegionStart7
+ dw Green_Instrument0Loop ; Loop to silence.
+Green_DisarkPointerRegionEnd7
+
+Green_Instrument3
+ db 0 ; Speed.
+
+ db 177 ; Volume: 12.
+ db 27 ; Arpeggio: 13.
+ db 9 ; Noise: 9.
+
+ db 173 ; Volume: 11.
+ db 18 ; Arpeggio: 9.
+
+ db 169 ; Volume: 10.
+ db 12 ; Arpeggio: 6.
+
+ db 165 ; Volume: 9.
+ db 9 ; Arpeggio: 4.
+ db 5 ; Noise: 5.
+
+ db 4 ; End the instrument.
+Green_DisarkPointerRegionStart8
+ dw Green_Instrument0Loop ; Loop to silence.
+Green_DisarkPointerRegionEnd8
+
+Green_Instrument4
+ db 0 ; Speed.
+
+ db 113 ; Volume: 12.
+ dw 2 ; Pitch: 2.
+
+ db 45 ; Volume: 11.
+
+ db 41 ; Volume: 10.
+
+ db 37 ; Volume: 9.
+
+ db 33 ; Volume: 8.
+
+ db 29 ; Volume: 7.
+
+ db 25 ; Volume: 6.
+
+ db 21 ; Volume: 5.
+
+ db 4 ; End the instrument.
+Green_DisarkPointerRegionStart9
+ dw Green_Instrument0Loop ; Loop to silence.
+Green_DisarkPointerRegionEnd9
+
+Green_DisarkByteRegionEnd4
+Green_ArpeggioIndexes
+Green_DisarkPointerRegionStart10
+Green_DisarkPointerRegionEnd10
+
+Green_DisarkByteRegionStart11
+Green_DisarkByteRegionEnd11
+
+Green_PitchIndexes
+Green_DisarkPointerRegionStart12
+Green_DisarkPointerRegionEnd12
+
+Green_DisarkByteRegionStart13
+Green_DisarkByteRegionEnd13
+
+; Green, Subsong 0.
+; ----------------------------------
+
+Green_Subsong0
+Green_Subsong0DisarkPointerRegionStart0
+ dw Green_Subsong0_NoteIndexes ; Index table for the notes.
+ dw Green_Subsong0_TrackIndexes ; Index table for the Tracks.
+Green_Subsong0DisarkPointerRegionEnd0
+
+Green_Subsong0DisarkByteRegionStart1
+ db 6 ; Initial speed.
+
+ db 2 ; Most used instrument.
+ db 4 ; Second most used instrument.
+
+ db 1 ; Most used wait.
+ db 3 ; Second most used wait.
+
+ db 0 ; Default start note in tracks.
+ db 0 ; Default start instrument in tracks.
+ db 0 ; Default start wait in tracks.
+
+ db 13 ; Are there effects? 12 if yes, 13 if not. Don't ask.
+Green_Subsong0DisarkByteRegionEnd1
+
+; The Linker.
+Green_Subsong0DisarkByteRegionStart2
+; Pattern 0
+Green_Subsong0_Loop
+ db 170 ; State byte.
+ db 63 ; New height.
+ db 128 ; New track (0) for channel 1, as a reference (index 0).
+ db 128 ; New track (0) for channel 2, as a reference (index 0).
+ db 128 ; New track (0) for channel 3, as a reference (index 0).
+
+ db 1 ; End of the Song.
+ db 0 ; Speed to 0, meaning "end of song".
+Green_Subsong0DisarkByteRegionEnd2
+Green_Subsong0DisarkPointerRegionStart3
+ dw Green_Subsong0_Loop
+
+Green_Subsong0DisarkPointerRegionEnd3
+; The indexes of the tracks.
+Green_Subsong0_TrackIndexes
+Green_Subsong0DisarkPointerRegionStart4
+ dw Green_Subsong0_Track0 ; Track 0, index 0.
+Green_Subsong0DisarkPointerRegionEnd4
+
+Green_Subsong0DisarkByteRegionStart5
+Green_Subsong0_Track0
+ db 205 ; New wait (127).
+ db 127 ; Escape wait value.
+
+Green_Subsong0DisarkByteRegionEnd5
+; The note indexes.
+Green_Subsong0_NoteIndexes
+Green_Subsong0DisarkByteRegionStart6
+Green_Subsong0DisarkByteRegionEnd6
+
+; Green, Subsong 1.
+; ----------------------------------
+
+Green_Subsong1
+Green_Subsong1DisarkPointerRegionStart0
+ dw Green_Subsong1_NoteIndexes ; Index table for the notes.
+ dw Green_Subsong1_TrackIndexes ; Index table for the Tracks.
+Green_Subsong1DisarkPointerRegionEnd0
+
+Green_Subsong1DisarkByteRegionStart1
+ db 6 ; Initial speed.
+
+ db 2 ; Most used instrument.
+ db 4 ; Second most used instrument.
+
+ db 1 ; Most used wait.
+ db 3 ; Second most used wait.
+
+ db 57 ; Default start note in tracks.
+ db 0 ; Default start instrument in tracks.
+ db 0 ; Default start wait in tracks.
+
+ db 13 ; Are there effects? 12 if yes, 13 if not. Don't ask.
+Green_Subsong1DisarkByteRegionEnd1
+
+; The Linker.
+Green_Subsong1DisarkByteRegionStart2
+; Pattern 0
+Green_Subsong1_Loop
+ db 170 ; State byte.
+ db 63 ; New height.
+ db ((Green_Subsong1_Track0 - ($ + 2)) & #ff00) / 256 ; New track (0) for channel 1, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong1_Track0 - ($ + 1)) & 255)
+ db ((Green_Subsong1_Track1 - ($ + 2)) & #ff00) / 256 ; New track (1) for channel 2, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong1_Track1 - ($ + 1)) & 255)
+ db 128 ; New track (2) for channel 3, as a reference (index 0).
+
+; Pattern 1
+ db 32 ; State byte.
+ db ((Green_Subsong1_Track3 - ($ + 2)) & #ff00) / 256 ; New track (3) for channel 2, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong1_Track3 - ($ + 1)) & 255)
+
+; Pattern 2
+ db 40 ; State byte.
+ db ((Green_Subsong1_Track4 - ($ + 2)) & #ff00) / 256 ; New track (4) for channel 1, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong1_Track4 - ($ + 1)) & 255)
+ db ((Green_Subsong1_Track1 - ($ + 2)) & #ff00) / 256 ; New track (1) for channel 2, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong1_Track1 - ($ + 1)) & 255)
+
+; Pattern 3
+ db 40 ; State byte.
+ db ((Green_Subsong1_Track5 - ($ + 2)) & #ff00) / 256 ; New track (5) for channel 1, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong1_Track5 - ($ + 1)) & 255)
+ db ((Green_Subsong1_Track3 - ($ + 2)) & #ff00) / 256 ; New track (3) for channel 2, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong1_Track3 - ($ + 1)) & 255)
+
+ db 1 ; End of the Song.
+ db 0 ; Speed to 0, meaning "end of song".
+Green_Subsong1DisarkByteRegionEnd2
+Green_Subsong1DisarkPointerRegionStart3
+ dw Green_Subsong1_Loop
+
+Green_Subsong1DisarkPointerRegionEnd3
+; The indexes of the tracks.
+Green_Subsong1_TrackIndexes
+Green_Subsong1DisarkPointerRegionStart4
+ dw Green_Subsong1_Track2 ; Track 2, index 0.
+Green_Subsong1DisarkPointerRegionEnd4
+
+Green_Subsong1DisarkByteRegionStart5
+Green_Subsong1_Track0
+ db 222 ; Primary instrument (2). New escaped note: 69. New wait (11).
+ db 69 ; Escape note value.
+ db 11 ; Escape wait value.
+ db 95 ; Primary instrument (2). Same escaped note: 69. Primary wait (1).
+ db 94 ; Primary instrument (2). New escaped note: 60. Primary wait (1).
+ db 60 ; Escape note value.
+ db 30 ; Primary instrument (2). New escaped note: 62.
+ db 62 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 69. Primary wait (1).
+ db 69 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 62. Primary wait (1).
+ db 62 ; Escape note value.
+ db 222 ; Primary instrument (2). New escaped note: 67. New wait (15).
+ db 67 ; Escape note value.
+ db 15 ; Escape wait value.
+ db 95 ; Primary instrument (2). Same escaped note: 67. Primary wait (1).
+ db 95 ; Primary instrument (2). Same escaped note: 67. Primary wait (1).
+ db 95 ; Primary instrument (2). Same escaped note: 67. Primary wait (1).
+ db 94 ; Primary instrument (2). New escaped note: 65. Primary wait (1).
+ db 65 ; Escape note value.
+ db 158 ; Primary instrument (2). New escaped note: 62. Secondary wait (3).
+ db 62 ; Escape note value.
+ db 222 ; Primary instrument (2). New escaped note: 60. New wait (127).
+ db 60 ; Escape note value.
+ db 127 ; Escape wait value.
+
+Green_Subsong1_Track1
+ db 94 ; Primary instrument (2). New escaped note: 38. Primary wait (1).
+ db 38 ; Escape note value.
+ db 190 ; New instrument (1). New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 190 ; New instrument (1). New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 38. Primary wait (1).
+ db 38 ; Escape note value.
+ db 142 ; New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 190 ; New instrument (1). New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 43. Primary wait (1).
+ db 43 ; Escape note value.
+ db 142 ; New escaped note: 31. Secondary wait (3).
+ db 31 ; Escape note value.
+ db 78 ; New escaped note: 29. Primary wait (1).
+ db 29 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 190 ; New instrument (1). New escaped note: 31. Secondary wait (3).
+ db 31 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 29. Primary wait (1).
+ db 29 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 43. Primary wait (1).
+ db 43 ; Escape note value.
+ db 142 ; New escaped note: 31. Secondary wait (3).
+ db 31 ; Escape note value.
+ db 78 ; New escaped note: 29. Primary wait (1).
+ db 29 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 126 ; New instrument (1). New escaped note: 31. Primary wait (1).
+ db 31 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 254 ; New instrument (1). New escaped note: 27. New wait (127).
+ db 27 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 127 ; Escape wait value.
+
+Green_Subsong1_Track2
+ db 205 ; New wait (127).
+ db 127 ; Escape wait value.
+
+Green_Subsong1_Track3
+ db 94 ; Primary instrument (2). New escaped note: 38. Primary wait (1).
+ db 38 ; Escape note value.
+ db 190 ; New instrument (1). New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 190 ; New instrument (1). New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 38. Primary wait (1).
+ db 38 ; Escape note value.
+ db 142 ; New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 190 ; New instrument (1). New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 43. Primary wait (1).
+ db 43 ; Escape note value.
+ db 142 ; New escaped note: 31. Secondary wait (3).
+ db 31 ; Escape note value.
+ db 78 ; New escaped note: 29. Primary wait (1).
+ db 29 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 190 ; New instrument (1). New escaped note: 31. Secondary wait (3).
+ db 31 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 29. Primary wait (1).
+ db 29 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 43. Primary wait (1).
+ db 43 ; Escape note value.
+ db 142 ; New escaped note: 31. Secondary wait (3).
+ db 31 ; Escape note value.
+ db 78 ; New escaped note: 29. Primary wait (1).
+ db 29 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 143 ; Same escaped note: 36. Secondary wait (3).
+ db 207 ; Same escaped note: 36. New wait (127).
+ db 127 ; Escape wait value.
+
+Green_Subsong1_Track4
+ db 111 ; Secondary instrument (4). Same escaped note: 57. Primary wait (1).
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 48. Primary wait (1).
+ db 48 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 57. Primary wait (1).
+ db 57 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 48. Primary wait (1).
+ db 48 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 69. Primary wait (1).
+ db 69 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 70. Primary wait (1).
+ db 70 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 69. Primary wait (1).
+ db 69 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 67. Primary wait (1).
+ db 67 ; Escape note value.
+ db 158 ; Primary instrument (2). New escaped note: 69. Secondary wait (3).
+ db 69 ; Escape note value.
+ db 158 ; Primary instrument (2). New escaped note: 65. Secondary wait (3).
+ db 65 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 55. Primary wait (1).
+ db 55 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 53. Primary wait (1).
+ db 53 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 55. Primary wait (1).
+ db 55 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 57. Primary wait (1).
+ db 57 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 55. Primary wait (1).
+ db 55 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 53. Primary wait (1).
+ db 53 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 48. Primary wait (1).
+ db 48 ; Escape note value.
+ db 158 ; Primary instrument (2). New escaped note: 62. Secondary wait (3).
+ db 62 ; Escape note value.
+ db 158 ; Primary instrument (2). New escaped note: 69. Secondary wait (3).
+ db 69 ; Escape note value.
+ db 95 ; Primary instrument (2). Same escaped note: 69. Primary wait (1).
+ db 94 ; Primary instrument (2). New escaped note: 67. Primary wait (1).
+ db 67 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 65. Primary wait (1).
+ db 65 ; Escape note value.
+ db 222 ; Primary instrument (2). New escaped note: 64. New wait (127).
+ db 64 ; Escape note value.
+ db 127 ; Escape wait value.
+
+Green_Subsong1_Track5
+ db 111 ; Secondary instrument (4). Same escaped note: 57. Primary wait (1).
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 48. Primary wait (1).
+ db 48 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 57. Primary wait (1).
+ db 57 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 48. Primary wait (1).
+ db 48 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 69. Primary wait (1).
+ db 69 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 70. Primary wait (1).
+ db 70 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 69. Primary wait (1).
+ db 69 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 67. Primary wait (1).
+ db 67 ; Escape note value.
+ db 158 ; Primary instrument (2). New escaped note: 69. Secondary wait (3).
+ db 69 ; Escape note value.
+ db 158 ; Primary instrument (2). New escaped note: 65. Secondary wait (3).
+ db 65 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 55. Primary wait (1).
+ db 55 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 53. Primary wait (1).
+ db 53 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 55. Primary wait (1).
+ db 55 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 57. Primary wait (1).
+ db 57 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 55. Primary wait (1).
+ db 55 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 53. Primary wait (1).
+ db 53 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 48. Primary wait (1).
+ db 48 ; Escape note value.
+ db 222 ; Primary instrument (2). New escaped note: 62. New wait (5).
+ db 62 ; Escape note value.
+ db 5 ; Escape wait value.
+ db 94 ; Primary instrument (2). New escaped note: 65. Primary wait (1).
+ db 65 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 67. Primary wait (1).
+ db 67 ; Escape note value.
+ db 159 ; Primary instrument (2). Same escaped note: 67. Secondary wait (3).
+ db 222 ; Primary instrument (2). New escaped note: 65. New wait (127).
+ db 65 ; Escape note value.
+ db 127 ; Escape wait value.
+
+Green_Subsong1DisarkByteRegionEnd5
+; The note indexes.
+Green_Subsong1_NoteIndexes
+Green_Subsong1DisarkByteRegionStart6
+Green_Subsong1DisarkByteRegionEnd6
+
+; Green, Subsong 2.
+; ----------------------------------
+
+Green_Subsong2
+Green_Subsong2DisarkPointerRegionStart0
+ dw Green_Subsong2_NoteIndexes ; Index table for the notes.
+ dw Green_Subsong2_TrackIndexes ; Index table for the Tracks.
+Green_Subsong2DisarkPointerRegionEnd0
+
+Green_Subsong2DisarkByteRegionStart1
+ db 6 ; Initial speed.
+
+ db 2 ; Most used instrument.
+ db 4 ; Second most used instrument.
+
+ db 1 ; Most used wait.
+ db 3 ; Second most used wait.
+
+ db 31 ; Default start note in tracks.
+ db 3 ; Default start instrument in tracks.
+ db 0 ; Default start wait in tracks.
+
+ db 13 ; Are there effects? 12 if yes, 13 if not. Don't ask.
+Green_Subsong2DisarkByteRegionEnd1
+
+; The Linker.
+Green_Subsong2DisarkByteRegionStart2
+; Pattern 0
+ db 170 ; State byte.
+ db 63 ; New height.
+ db ((Green_Subsong2_Track0 - ($ + 2)) & #ff00) / 256 ; New track (0) for channel 1, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong2_Track0 - ($ + 1)) & 255)
+ db ((Green_Subsong2_Track1 - ($ + 2)) & #ff00) / 256 ; New track (1) for channel 2, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong2_Track1 - ($ + 1)) & 255)
+ db 128 ; New track (2) for channel 3, as a reference (index 0).
+
+; Pattern 1
+Green_Subsong2_Loop
+ db 40 ; State byte.
+ db 128 ; New track (2) for channel 1, as a reference (index 0).
+ db 128 ; New track (2) for channel 2, as a reference (index 0).
+
+ db 1 ; End of the Song.
+ db 0 ; Speed to 0, meaning "end of song".
+Green_Subsong2DisarkByteRegionEnd2
+Green_Subsong2DisarkPointerRegionStart3
+ dw Green_Subsong2_Loop
+
+Green_Subsong2DisarkPointerRegionEnd3
+; The indexes of the tracks.
+Green_Subsong2_TrackIndexes
+Green_Subsong2DisarkPointerRegionStart4
+ dw Green_Subsong2_Track2 ; Track 2, index 0.
+Green_Subsong2DisarkPointerRegionEnd4
+
+Green_Subsong2DisarkByteRegionStart5
+Green_Subsong2_Track0
+ db 110 ; Secondary instrument (4). New escaped note: 55. Primary wait (1).
+ db 55 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 54. Primary wait (1).
+ db 54 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 53. Primary wait (1).
+ db 53 ; Escape note value.
+ db 46 ; Secondary instrument (4). New escaped note: 51.
+ db 51 ; Escape note value.
+ db 46 ; Secondary instrument (4). New escaped note: 49.
+ db 49 ; Escape note value.
+ db 174 ; Secondary instrument (4). New escaped note: 50. Secondary wait (3).
+ db 50 ; Escape note value.
+ db 175 ; Secondary instrument (4). Same escaped note: 50. Secondary wait (3).
+ db 238 ; Secondary instrument (4). New escaped note: 55. New wait (7).
+ db 55 ; Escape note value.
+ db 7 ; Escape wait value.
+ db 222 ; Primary instrument (2). New escaped note: 43. New wait (127).
+ db 43 ; Escape note value.
+ db 127 ; Escape wait value.
+
+Green_Subsong2_Track1
+ db 79 ; Same escaped note: 31. Primary wait (1).
+ db 126 ; New instrument (1). New escaped note: 30. Primary wait (1).
+ db 30 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 29. Primary wait (1).
+ db 29 ; Escape note value.
+ db 78 ; New escaped note: 28. Primary wait (1).
+ db 28 ; Escape note value.
+ db 190 ; New instrument (3). New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 191 ; New instrument (1). Same escaped note: 26. Secondary wait (3).
+ db 1 ; Escape instrument value.
+ db 254 ; New instrument (3). New escaped note: 43. New wait (7).
+ db 43 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 7 ; Escape wait value.
+ db 254 ; New instrument (1). New escaped note: 31. New wait (127).
+ db 31 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 127 ; Escape wait value.
+
+Green_Subsong2_Track2
+ db 205 ; New wait (127).
+ db 127 ; Escape wait value.
+
+Green_Subsong2DisarkByteRegionEnd5
+; The note indexes.
+Green_Subsong2_NoteIndexes
+Green_Subsong2DisarkByteRegionStart6
+Green_Subsong2DisarkByteRegionEnd6
+
diff --git a/game/src/song_playerconfig.asm b/game/src/song_playerconfig.asm
new file mode 100644
index 0000000..9e7821f
--- /dev/null
+++ b/game/src/song_playerconfig.asm
@@ -0,0 +1,17 @@
+; Configuration that can be included to Arkos Tracker 2 players.
+; It indicates what parts of code are useful to the song/sound effects, to save both memory and CPU.
+; The players may or may not take advantage of these flags, it is up to them.
+
+; You can either:
+; - Include this to the source that also includes the player (BEFORE the player is included) (recommended solution).
+; - Include this at the beginning of the player code.
+; - Copy/paste this directly in the player.
+; If you use one player but several songs, don't worry, these declarations will stack up.
+; If no configuration is used, the player will use default values (full code used).
+
+ PLY_CFG_ConfigurationIsPresent = 1
+ PLY_CFG_NoSoftNoHard = 1
+ PLY_CFG_SoftOnly = 1
+ PLY_CFG_SoftOnly_Noise = 1
+ PLY_CFG_SoftOnly_SoftwareArpeggio = 1
+ PLY_CFG_SoftOnly_SoftwarePitch = 1
diff --git a/include/mplayer.h b/include/mplayer.h
new file mode 100644
index 0000000..a8c1dc4
--- /dev/null
+++ b/include/mplayer.h
@@ -0,0 +1,181 @@
+/*
+ * mplayer lib
+ * Copyright (C) 2020 by Juan J. Martinez <jjm@usebox.net>
+ *
+ * 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.
+ *
+ * Arkos 2 AKM
+ * Copyright (c) 2016-2020 Julien Névo (contact@julien-nevo.com)
+ * (check mplayer/akm/LICENSE.txt; MIT License)
+ */
+
+#ifndef _MPLAYER_H
+#define _MPLAYER_H
+
+#include <stdint.h>
+
+// @Initialization
+
+/**
+ * Sets the player to play `sub_song` from `song`.
+ *
+ * `sub_song` starts from 0.
+ *
+ * This function can be called as many times as needed, for example, to change
+ * the playback to a different sub-song.
+ *
+ * It is required to call it at least once before using
+ * [mplayer_play](#mplayer_play).
+ *
+ * Example:
+ *
+ * ```c
+ * // tell the compiler we will use this
+ * // global symbol
+ * extern uint8_t SONG[];
+ *
+ * mplayer_init(SONG, 0);
+ * ```
+ *
+ * See [exporting songs and effects](#exporting-songs-and-effects) for details
+ * on how to add songs to your project.
+ */
+void mplayer_init(uint8_t *song, uint8_t sub_song);
+
+/**
+ * Inits the effects table.
+ *
+ * It is required to call it least once before using
+ * [mplayer_play](#mplayer_play) or any of the play effect functions.
+ *
+ * Example:
+ *
+ * ```c
+ * // tell the compiler we will use this
+ * // global symbol
+ * extern uint8_t EFFECTS[];
+ *
+ * mplayer_init_effects(EFFECTS);
+ * ```
+ *
+ * See [exporting songs and effects](#exporting-songs-and-effects) for details
+ * on how to add effects to your project.
+ */
+void mplayer_init_effects(uint8_t *effects) __z88dk_fastcall;
+
+// @Playback
+
+/**
+ * Plays a frame using the PSG.
+ *
+ * This must be called from the interrupt handler. See
+ * [set_user_isr](ubox-lib-ref.html#ubox_set_user_isr).
+ *
+ * [mplayer_init](#mplayer_init) must be called before using it.
+ *
+ * If the player is stopped, [mplayer_stop](#mplayer_stop) must be called to
+ * silence the PSG.
+ */
+void mplayer_play();
+
+/**
+ * Silences the PSG, stopping any sound.
+ *
+ * This doesn't stop the player if [mplayer_play](#mplayer_play) is called.
+ */
+void mplayer_stop();
+
+// @Sound effects
+
+/**
+ * Plays `effect_no` on `chan` channel at `inv_vol` volume.
+ *
+ * `effect_no` starts on 1 and refers to the index in the effects table set by
+ * [mplayer_init_effects](#mplayer_init_effects).
+ *
+ * `chan` can be 0, 1 or 2. It is recommended to use one channel for effects
+ * and the other two for the music.
+ *
+ * `inv_vol` is the inverted volume, with 0 for max volume and 16 for no sound.
+ *
+ * If an effects is already playing on the channel, it will be interrupted and
+ * replaced by this effect.
+ *
+ * The effects table must be set with
+ * [mplayer_init_effects](#mplayer_init_effects) before using this function.
+ *
+ * See [mplayer_play_effect_p](#mplayer_play_effect_p) for playing effects with
+ * priority.
+ *
+ * Example:
+ *
+ * ```c
+ * // plays effect 1 on channel 2 at highest volume
+ * mplayer_play_effect(1, 2, 0);
+ * ```
+ */
+void mplayer_play_effect(uint8_t effect_no, uint8_t chan, uint8_t inv_vol);
+
+/**
+ * Plays `effect_no` on `chan` channel at `inv_vol` volume using priority.
+ *
+ * `effect_no` starts on 1 and refers to the index in the effects table set by
+ * [mplayer_init_effects](#mplayer_init_effects).
+ *
+ * `effect_no` is used as priority, being effect number 1 the one with
+ * lowest priority.
+ *
+ * `chan` can be 0, 1 or 2. It is recommended to use one channel for effects
+ * and the other two for the music.
+ *
+ * `inv_vol` is the inverted volume, with 0 for max volume and 16 for no sound.
+ *
+ * The effect is played if no effect is already being played or if the effect
+ * being played has less priority. In that case, the effect currently being
+ * played will be interrupted and replaced by this effect.
+ *
+ * The effects table must be set with
+ * [mplayer_init_effects](#mplayer_init_effects) before using this function.
+ *
+ * See [mplayer_play_effect](#mplayer_play_effect) for playing effects without
+ * priority.
+ *
+ * Example:
+ *
+ * ```c
+ * // plays effect 2 on channel 2 at highest volume
+ * mplayer_play_effect_p(2, 2, 0);
+ * // won't interrupt effect 2 because it is higher priority than 1
+ * mplayer_play_effect_p(1, 2, 0);
+ * ```
+ */
+void mplayer_play_effect_p(uint8_t effect_no, uint8_t chan, uint8_t inv_vol);
+
+/**
+ * Stops the effect being played on `chan` channel.
+ */
+void mplayer_stop_effect_channel(uint8_t chan) __z88dk_fastcall;
+
+/**
+ * Returns 0 if there's no sound effect being played on `chan` channel, or
+ * a value different than 0 otherwise.
+ */
+uint8_t mplayer_is_sound_effect_on(uint8_t chan) __z88dk_fastcall;
+
+#endif // _MPLAYER_H
diff --git a/include/spman.h b/include/spman.h
new file mode 100644
index 0000000..67f5e6d
--- /dev/null
+++ b/include/spman.h
@@ -0,0 +1,149 @@
+/*
+ * spman lib
+ * Copyright (C) 2020 by Juan J. Martinez <jjm@usebox.net>
+ *
+ * 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 _SPMAN_H
+#define _SPMAN_H
+
+#include <stdint.h>
+
+#include "ubox.h"
+
+// @Initialization and pattern allocation
+
+/**
+ * Initialize **spman**.
+ *
+ * Sets all the patterns as unused and no allocated sprites.
+ *
+ * it needs to be called before using any of the functions of the sprite manager.
+ */
+void spman_init();
+
+/**
+ * Allocates a pattern for sprite `type` using `data`.
+ *
+ * `len` specifies the number of sprite patterns to allocate (16x16 each,
+ * exactly 32 bytes).
+ *
+ * If `flip` is set to a non zero value, the pattern data will be flipped
+ * horizontally.
+ *
+ * The function returns the allocated index in the pattern table, than can be
+ * used in [struct sprite_attr](ubox-lib-ref.html#struct-sprite_attr). It
+ * can be called multiple times for the same `type` and the allocation happens
+ * only once per `type`, returning the previously allocated index.
+ *
+ * `type` is an abstraction to group patterns. For example, if our game's
+ * player character has 3 animation frames per horizontal direction, we could
+ * use two different types to group those.
+ *
+ * Example:
+ * ```c
+ * enum sprite_types
+ * {
+ * SPRITE_PLAYER = 0,
+ * SPRITE_PLAYER_FLIP,
+ * SPRITE_ENEMY,
+ * }:
+ *
+ * spman_alloc_pat(SPRITE_PLAYER, player_sprite, 3, 0);
+ * // allocate the same frames flipped horizontally, on the next type
+ * spman_alloc_pat(SPRITE_PLAYER_FLIP, player_sprite, 3, 1);
+ *
+ * // only one frame, keep the allocated pattern
+ * uint8_t enemy_pat = spman_alloc_pat(SPRITE_ENEMY, enemy_sprite, 1, 0);
+ * ```
+ *
+ * A `type` can't be reused with a different pattern, [spman_init](#spman_init)
+ * has to be called again to free all the patterns.
+ */
+uint8_t spman_alloc_pat(uint8_t type, uint8_t *data, uint8_t len, uint8_t flip);
+
+// @Sprite allocation
+
+/**
+ * Allocates a sprite described by `sp` to be shown on the screen on the next
+ * call to [spman_update](#spman_update). This sprite will be excluded from
+ * flickering.
+ *
+ * It is recommended that the number of fixes sprites per line is below 4, or
+ * some sprites may never be shown.
+ *
+ * The limit of sprites to be allocated is 31, including non-fixed sprites.
+ *
+ * The pattern specified in the struct should be allocated with
+ * [spman_alloc_pat](#spman_alloc_pat).
+ *
+ * Allocated sprites are shown on screen by calling to
+ * [spman_update](#spman_update).
+ */
+void spman_alloc_fixed_sprite(struct sprite_attr *sp);
+
+/**
+ * Allocates a sprite described by `sp` to be shown on the screen on the next
+ * call to [spman_update](#spman_update).
+ *
+ * If more than 4 sprites are drawn on the same line, the sprite manager will
+ * alternate which ones are drawn, resulting on a "flickering" effect.
+ *
+ * The limit of sprites to be allocated is 31, including fixed sprites.
+ *
+ * The pattern specified in the struct should be allocated with
+ * [spman_alloc_pat](#spman_alloc_pat).
+ *
+ * Allocated sprites are shown on screen by calling to
+ * [spman_update](#spman_update).
+ */
+void spman_alloc_sprite(struct sprite_attr *sp);
+
+/**
+ * Any allocated sprite will be freed.
+ *
+ * This doesn't affect sprites currently being shown on the screen.
+ */
+void spman_sprite_flush();
+
+// @Sprites on screen
+
+/**
+ * Allocated sprites are processed, making the changes visible on screen.
+ *
+ * Once the changes have been applied, all the allocations are freed. This
+ * doesn't affect the sprites being shown on screen.
+ *
+ * Sprites must be allocated with
+ * [spman_alloc_fixed_sprite](#spman_alloc_fixed_sprite) and
+ * [spman_alloc_sprite](#spman_alloc_sprite).
+ *
+ */
+void spman_update();
+
+/**
+ * Hides all the sprites currently shown on screen.
+ *
+ * This doesn't affect any allocated sprites. To free allocated sprites use
+ * [spman_sprite_flush](#spman_sprite_flush).
+ */
+void spman_hide_all_sprites();
+
+#endif // _SPMAN_H
diff --git a/include/ubox.h b/include/ubox.h
new file mode 100644
index 0000000..2a747c0
--- /dev/null
+++ b/include/ubox.h
@@ -0,0 +1,668 @@
+/*
+ * ubox MSX lib
+ * Copyright (C) 2020 by Juan J. Martinez <jjm@usebox.net>
+ *
+ * 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 _UBOX_MSX_H
+#define _UBOX_MSX_H
+
+#include <stdint.h>
+
+// @Screen and VDP functions
+//
+// These functions are not necessarily MSX 1 specific, but only MSX 1
+// functionality is documented.
+//
+// Both the tiles and sprite functions target Screen 2 (256x192 pixels graphics
+// mode).
+//
+// The VRAM layout for Screen 2 is as follows:
+//
+// | Address range | Desctiption |
+// | --- | --- |
+// | <tt>0x0000-0x17ff</tt> | Background tiles |
+// | <tt>0x1800-0x1aff</tt> | Background tile map |
+// | <tt>0x1b00-0x1b7f</tt> | Sprite attributes |
+// | <tt>0x2000-0x37ff</tt> | Background colors |
+// | <tt>0x3800-0x3fff</tt> | Sprite patterns |
+//
+
+/**
+ * Sets the screen mode to `mode`.
+ *
+ * This is a list of the different MSX 1 modes:
+ *
+ * | Mode | Name | Description |
+ * | --- | --- | --- |
+ * | 0 | Screen 0 | 40x24 text mode |
+ * | 1 | Screen 1 | 32x24 colored text mode |
+ * | 2 | Screen 2 | 256x192 graphics mode |
+ * | 3 | Screen 3 | 64*48 block graphics multicolor mode |
+ *
+ * Example:
+ *
+ * ```c
+ * // sets Screen 2
+ * ubox_set_mode(2);
+ * ```
+ */
+void ubox_set_mode(uint8_t mode) __z88dk_fastcall;
+
+/**
+ * Enables the screen.
+ */
+void ubox_enable_screen();
+
+/**
+ * Disables the screen.
+ *
+ * Any content drawn to the screen won't be visible until the screen is enabled
+ * with [ubox_enable_screen](#ubox_enable_screen).
+ *
+ * Note that [ubox_set_mode](#ubox_set_mode) also enables the screen.
+ */
+void ubox_disable_screen();
+
+/**
+ * Changes the screen colors.
+ *
+ * First it sets `FORCLR` (foreground) to `fg`, `BAKCLR` (background) to `bg`
+ * and `BDRCLR` to `border`, and then makes them active.
+ *
+ * Example:
+ *
+ * ```c
+ * // sets all three colours to black
+ * ubox_set_colors(1, 1, 1);
+ * ```
+ */
+void ubox_set_colors(uint8_t fg, uint8_t bg, uint8_t border);
+
+/**
+ * Writes a block of `len` bytes from `src` in memory to `dst` in VRAM (video
+ * memory).
+ *
+ * Example:
+ * ```c
+ * // copy 4 sprites attributes from a buffer to the
+ * // sprite attribute table in Screen 2 mode
+ * ubox_write_vm((uint8_t*)0x1b00, 4 * 4, buffer);
+ * ```
+ */
+void ubox_write_vm(uint8_t *dst, uint16_t len, uint8_t *src);
+
+/**
+ * Reads a block of `len` bytes from `src` in VMEM (video memory) to `dst`
+ * in memory.
+ */
+void ubox_read_vm(uint8_t *dst, uint16_t len, uint8_t *src);
+
+/**
+ * Writes `data` to the `reg` VDP register.
+ *
+ * This function is used to setup the VDP, for example to enable 16x16 sprites.
+ *
+ * The VDP has 8 registers, from 0 to 7. For example, it is common to use
+ * register 1 to enable 16x16 sprites.
+ *
+ * The control bits in register 1 are:
+ *
+ * | Bit | Description |
+ * | --- | --- |
+ * | 0 | Enable sprite zoom (x2) |
+ * | 1 | Enable 16x16 sprites (default is 8x8) |
+ * | 2 | Not used |
+ * | 3 | Enable Mode M2, Screen 3 (block) |
+ * | 4 | Enable Mode M1, Screen 0 (text) |
+ * | 5 | Enable v-blank interrupt |
+ * | 6 | Enable screen output control |
+ * | 7 | VRAM size control (0: 4K, 1: 16K) |
+ *
+ * Please refer to [Portar
+ * Doc](http://problemkaputt.de/portar.htm#vdpregisters00h07hbasicmsx1msx2videoregisters)
+ * for further info on VDP registers.
+ *
+ * Example:
+ * ```c
+ * // reg 1: activate sprites, v-blank int on, 16x16 sprites
+ * ubox_wvdp(1, 0xe2);
+ * ```
+ */
+void ubox_wvdp(uint8_t reg, uint8_t data);
+
+/**
+ * Returns the default interrupt frequency according to the BIOS.
+ *
+ * Possible values are:
+ *
+ * | Value | Frequency |
+ * | --- | --- |
+ * | 0 | 60Hz |
+ * | 1 | 50Hz |
+ */
+uint8_t ubox_get_vsync_freq();
+
+// *INDENT-OFF*
+/**
+ * This macro waits for the *vsync* interrupt.
+ *
+ * Interrupts must be enabled or this code will block indefinitely.
+ *
+ * Example:
+ * ```c
+ * ubox_wait_vsync();
+ * // code to run after the int
+ * ```
+ */
+#define ubox_wait_vsync() do { \
+ __asm; \
+ halt \
+ __endasm; \
+} while(0)
+// *INDENT-ON*
+
+// @Tile functions
+//
+// All the functions require **screen 2** (256x192 graphics mode, see
+// [ubox_set_mode](#ubox_set_mode)).
+
+/**
+ * Sets the current tile set to the tile set pointed by `tiles`.
+ *
+ * The tile set is expected to have 256 tiles (8x8 pixels, 8 bytes per tile)
+ * and it will be loaded into the VDP in all three screen sections.
+ *
+ * All the tiles functions use an index in this tile set (usually via a `tile`
+ * parameter).
+ */
+void ubox_set_tiles(uint8_t *tiles) __z88dk_fastcall;
+
+/**
+ * Sets the current color table for current tile set to the color table pointed
+ * by `colors`.
+ *
+ * The color table is expected to have the 8 color rows for each of the 256 tiles
+ * of the tiles table (8 rows, 8 bytes per tile). The colors will be loaded into
+ * the VDP in all three screen sections.
+ */
+void ubox_set_tiles_colors(uint8_t *colors) __z88dk_fastcall;
+
+/**
+ * Puts a tile from current tile set on the screen. The tile is identified by
+ * index `tile` and placed on position (`x`,`y`).
+ *
+ * `x` and `y` units are tiles, and both are 0 based.
+ *
+ * Example:
+ * ```c
+ * // put the tile with index 1 on the top left corner of the screen
+ * ubox_put_tile(0, 0, 1);
+ * ```
+ *
+ * This function is expensive and it performs two calls to the VDP that must
+ * keep the state between them. In case the interrupt handler performs
+ * operations with the VDP, this function shouldn't be used.
+ *
+ * To put multiple tiles in a row, use [ubox_write_vm](#ubox_write_vm) instead. The
+ * tile map is in the memory address `0x1800`.
+ *
+ * Example putting multiple tiles:
+ * ```c
+ * ubox_wait_vsync();
+ * // write a complete row of tiles (32) from a buffer
+ * ubox_write_vm((uint8_t *)0x1800, 32, buffer);
+ * ```
+ */
+void ubox_put_tile(uint8_t x, uint8_t y, uint8_t tile);
+
+/**
+ * Returns the tile index at position (`x`, `y`).
+ *
+ * `x` and `y` units are tiles, and both are 0 based.
+ */
+uint8_t ubox_get_tile(uint8_t x, uint8_t y);
+
+/**
+ * Fills the screen with the tile from current tile set identified by `tile` index.
+ *
+ * See [ubox_set_tiles](#ubox_set_tiles) for for information on how to set a
+ * tile set.
+ */
+void ubox_fill_screen(uint8_t tile) __z88dk_fastcall;
+
+// @Interrupt and clock functions
+
+/**
+ * Installs and initializes the interrupt handler.
+ *
+ * `wait_ticks` is the maximum number of interrupts that [ubox_wait](#ubox_wait)
+ * will wait. This value divides the frequency at which the machine generates the *vsync*
+ * interrupt (50Hz for PAL and 60Hz for NTSC).
+ *
+ * Some common values are:
+ *
+ * | wait ticks | PAL | NTSC |
+ * | --- | --- | --- |
+ * | 1 | 50 FPS | 60 FPS |
+ * | 2 | 25 FPS | 30 FPS |
+ * | 3 | 16.6 FPS | 20 FPS |
+ *
+ * Where FPS is the number of *frames per second* desired to update the game logic.
+ *
+ * Example:
+ * ```c
+ * // ubox_wait will wait for 2 ints (25 FPS)
+ * ubox_init_isr(2);
+ * ```
+ *
+ * This function must be called once before any of the interrupt and clock
+ * functions are used.
+ *
+ * The installed interrupt handler uses `HTIMI_HOOK` instead of replacing the
+ * BIOS handler.
+ *
+ * The interrupt handler will save all registers. The shadow registers are not
+ * preserved or used.
+ *
+ * For performance reasons the BIOS keyboard buffer and key repeat functionality
+ * are disabled, and because of that the some BIOS functions, such as `CHGET`,
+ * won't work.
+ */
+void ubox_init_isr(uint8_t wait_ticks) __z88dk_fastcall;
+
+/**
+ * Installs an user interrupt handler.
+ *
+ * The function pointed by `fn` doesn't need to preserve any registers because
+ * that is done by the main interrupt handler.
+ *
+ * It is recommended that the user interrupt handler is short and uses the
+ * minimal amount of CPU cycles.
+ *
+ * Example:
+ *
+ * ```c
+ * uint8_t tick = 0;
+ *
+ * void my_isr()
+ * {
+ * // count interrupts
+ * ++tick;
+ * }
+ *
+ * ubox_set_user_isr(my_isr);
+ * ```
+ */
+void ubox_set_user_isr(void (*fn)()) __z88dk_fastcall;
+
+/**
+ * Waits for a maximum `wait_ticks` interrupts (see [ubox_init_isr](#ubox_init_isr)).
+ *
+ * This function counts the interrupts (ticks) between between current call to
+ * `ubox_wait` and the previous one, and if less than `wait_ticks` ticks have
+ * passed, it will wait.
+ *
+ * This is used to ensure that the game loop used time is constant and hence
+ * the game plays smooth in case some updates are faster than others.
+ *
+ * If the time passed between current `ubox_wait` call and the previous is
+ * larger than `wait_ticks`, no waiting happens. This may mean that the game
+ * loop takes longer than expected and, if that happens frequently, it would be
+ * recommended to increase `wait_ticks` value when calling
+ * [ubox_init_isr](#ubox_init_isr).
+ *
+ * Example:
+ * ```c
+ * // 25 FPS on PAL, 30 FPS on NTSC
+ * ubox_init_isr(2);
+ *
+ * // game loop example
+ * while (1)
+ * {
+ * update_controls();
+ *
+ * update_entities();
+ * draw_entities();
+ *
+ * // ensure that one loop takes at least 2 interrupts
+ * ubox_wait();
+ * }
+ * ```
+ */
+void ubox_wait();
+
+/**
+ * Waits for `frames` frames.
+ *
+ * This is used to wait a fixed amount of time measured in frames.
+ *
+ * Because `frames` is 8-bit value, this funcion can only wait for 255 frames
+ * in one single call.
+ *
+ * Example:
+ * ```c
+ * // assuming PAL and wait_ticks 2
+ * // wait for 10 seconds
+ * ubox_wait_for(250);
+ * ```
+ */
+void ubox_wait_for(uint8_t frames) __z88dk_fastcall;
+
+extern uint8_t ubox_tick;
+
+/**
+ * Sets `ubox_tick` to zero.
+ *
+ * `ubox_tick` is a 8-bit global variable that is incremented on every interrupt.
+ */
+void ubox_reset_tick();
+
+// @Sprite functions
+//
+// All the functions require **screen 2** (256x192 graphics mode, see
+// [ubox_set_mode](#ubox_set_mode)).
+
+/**
+ * Structure to define sprite attributes.
+ *
+ * | Field | Description |
+ * | --- | --- |
+ * | `x` | The horizontal position of the sprite on the screen. |
+ * | `y` | The vertical position of the sprite on the screen. This coordinate starts in -1 (0xff) instead of 0. |
+ * | `pattern` | The index in the sprite pattern table. When 16x16 sprites are enabled, the lower two bits are ignored (4 regular 8x8 sprites form one 16x16 sprite). |
+ * | `attr` | The sprite attributes. The bits 0-3 are used for color and bit 7 is EC (Early Clock). |
+ *
+ * When EC is enabled, the sprite is displaced 32 pixels to the left.
+ */
+struct sprite_attr {
+ uint8_t y;
+ uint8_t x;
+ uint8_t pattern;
+ uint8_t attr;
+};
+
+/**
+ * Sets the sprite pattern data pointed by `data` into pattern number
+ * `pattern`.
+ *
+ * `pattern` is the index in the sprite pattern table of Screen 2.
+ *
+ * This function will set a 8x8 pixels pattern.
+ *
+ * See [ubox_set_sprite_pat16](#ubox_set_sprite_pat16) to set a 16x16 pixels pattern.
+ */
+void ubox_set_sprite_pat8(uint8_t *data, uint8_t pattern);
+
+/**
+ * Sets the sprite pattern data pointed by `data` into pattern number
+ * `pattern`, flipping the pattern data horizontally.
+ *
+ * `pattern` is the index in the sprite pattern table of Screen 2.
+ *
+ * This function will set a 8x8 pixels pattern.
+ *
+ * See [ubox_set_sprite_pat16_flip](#ubox_set_sprite_pat16_flip) to set a 16x16
+ * pixels pattern.
+ */
+void ubox_set_sprite_pat8_flip(uint8_t *data, uint8_t pattern);
+
+/**
+ * Sets the sprite attributes of sprite number `sprite` using the attributes
+ * pointed by `attr`.
+ *
+ * `sprite` is the index in the sprite table of Screen 2.
+ *
+ * See [struct sprite_attr](#struct-sprite_attr) description for details on
+ * the sprite attributes.
+ *
+ * Example:
+ * ```c
+ * ubox_set_sprite_attr(&player_sprite_attr, 0);
+ * ```
+ *
+ * To set the attributes of multiple sprites that are contiguous in the sprite
+ * table, it is recommended to use [ubox_write_vm](#ubox_write_vm). The sprite
+ * attribute table is in the memory address `0x1b00`.
+ */
+void ubox_set_sprite_attr(struct sprite_attr *attr, uint8_t sprite);
+
+/**
+ * Sets the sprite pattern data pointed by `data` into pattern number
+ * `pattern`.
+ *
+ * `pattern` is the index in the sprite pattern table of Screen 2.
+ *
+ * This function will set a 16x16 pixels pattern. The pattern is equivalent
+ * to setting four 8x8 patterns (upper left, lower left, upper right and lower right).
+ *
+ * See [ubox_set_sprite_pat8](#ubox_set_sprite_pat8) to set a 8x8 pixels pattern.
+ */
+void ubox_set_sprite_pat16(uint8_t *data, uint8_t pattern);
+
+/**
+ * Sets the sprite pattern data pointed by `data` into pattern number
+ * `pattern`, flipping the pattern data horizontally.
+ *
+ * `pattern` is the index in the sprite pattern table of Screen 2.
+ *
+ * This function will set a 16x16 pixels pattern. The pattern is equivalent
+ * to setting four 8x8 patterns (upper left, lower left, upper right and lower right).
+ *
+ * See [ubox_set_sprite_pat8_flip](#ubox_set_sprite_pat8_flip) to set a 8x8
+ * pixels pattern.
+ */
+void ubox_set_sprite_pat16_flip(uint8_t *data, uint8_t pattern);
+
+/**
+ * Sets the sprite attributes of sprite number `sprite` using the attributes
+ * pointed by `attr`.
+ *
+ * `sprite` is the index in the sprite table of Screen 2.
+ *
+ * See [struct sprite_attr](#struct-sprite_attr) description for details on
+ * the sprite attributes.
+ *
+ * Example:
+ * ```c
+ * ubox_set_sprite_attr(&player_sprite_attr, 0);
+ * ```
+ *
+ * To set the attributes of multiple sprites that are contiguous in the sprite
+ * table, it is recommended to use [ubox_write_vm](#ubox_write_vm). The sprite
+ * attribute table is in the memory address `0x1b00`.
+ */
+void ubox_set_sprite_attr(struct sprite_attr *attr, uint8_t sprite);
+
+// @Control functions
+//
+// The supported controls are: cursor and joystick (port 1 and port 2).
+//
+// In the case of cursor, space key is used as fire 1 and the "m" key as fire 2.
+//
+// For one button joysticks, [ubox_read_keys](#ubox_read_keys) can be used to
+// provide an alternative fire 2 button using the keyboard.
+
+/**
+ * Waits for trigger on cursor (space or "m") or any of the joysticks, and
+ * returns the selected control.
+ *
+ * See [ubox_read_ctl](#ubox_read_ctl) for possible control values.
+ */
+uint8_t ubox_select_ctl();
+
+/**
+ * Read the control identified by `control` and return the status of it.
+ *
+ * Possible control values are:
+ *
+ * | Control | Description |
+ * | --- | --- |
+ * | UBOX_MSX_CTL_CURSOR | Cursor for move, space for fire 1 and "m" for fire 2 |
+ * | UBOX_MSX_CTL_PORT1 | Joystick on port 1 |
+ * | UBOX_MSX_CTL_PORT2 | Joystick on port 2 |
+ * | UBOX_MSX_CTL_NONE | No control is selected. This is the default output of [ubox_select_ctl](#ubox_select_ctl) |
+ *
+ * The control status is a bit map with the following values:
+ *
+ * | Mask | Bit |
+ * | --- | --- |
+ * | UBOX_MSX_CTL_UP | 0 |
+ * | UBOX_MSX_CTL_DOWN | 1 |
+ * | UBOX_MSX_CTL_LEFT | 2 |
+ * | UBOX_MSX_CTL_RIGHT | 3 |
+ * | UBOX_MSX_CTL_FIRE1 | 4 |
+ * | UBOX_MSX_CTL_FIRE2 | 5 |
+ *
+ * Example:
+ * ```c
+ * if (ubox_read_ctl(UBOX_MSX_CTL_CURSOR) & UBOX_MSX_CTL_UP)
+ * {
+ * // in the cursor, UP is on
+ * }
+ * ```
+ */
+uint8_t ubox_read_ctl(uint8_t control) __z88dk_fastcall;
+
+#define UBOX_MSX_CTL_CURSOR 0
+#define UBOX_MSX_CTL_PORT1 1
+#define UBOX_MSX_CTL_PORT2 2
+#define UBOX_MSX_CTL_NONE 0xff
+
+#define UBOX_MSX_CTL_UP 1
+#define UBOX_MSX_CTL_DOWN 2
+#define UBOX_MSX_CTL_LEFT 4
+#define UBOX_MSX_CTL_RIGHT 8
+#define UBOX_MSX_CTL_FIRE1 16
+#define UBOX_MSX_CTL_FIRE2 32
+
+/**
+ * Reads a row of the keyboard matrix and returns a bit map with the state of
+ * the keys in that row.
+ *
+ * It is important to remember that there are different keyboard layouts and
+ * that there is no reliable way of detecting 100% of them.
+ *
+ * Constants for the QWERTY layout are provided as `UBOX_MSX_KEY_*`, and that covers
+ * the most common models.
+ *
+ * Example:
+ * ```c
+ * // read row 7 that includes RET, SEL, BS, STOP, TAB, ESC, F5 and F4
+ * uint8_t keys = ubox_read_keys(7);
+ *
+ * if (keys & UBOX_MSX_KEY_ESC)
+ * {
+ * // ESC key is pressed
+ * }
+ * ```
+ */
+uint8_t ubox_read_keys(uint8_t row) __z88dk_fastcall;
+
+// row 0
+#define UBOX_MSX_KEY_7 0x80
+#define UBOX_MSX_KEY_6 0x40
+#define UBOX_MSX_KEY_5 0x20
+#define UBOX_MSX_KEY_4 0x10
+#define UBOX_MSX_KEY_3 0x08
+#define UBOX_MSX_KEY_2 0x04
+#define UBOX_MSX_KEY_1 0x02
+#define UBOX_MSX_KEY_0 0x01
+
+// row 1
+#define UBOX_MSX_KEY_SEMI 0x80 // ";"
+#define UBOX_MSX_KEY_CSBRACKET 0x40 // "]"
+#define UBOX_MSX_KEY_OSBRACKET 0x20 // "["
+#define UBOX_MSX_KEY_BSLASH 0x10 // "\"
+#define UBOX_MSX_KEY_EQUAL 0x08 // "="
+#define UBOX_MSX_KEY_MINUS 0x04 // "-"
+#define UBOX_MSX_KEY_9 0x02
+#define UBOX_MSX_KEY_8 0x01
+
+// row 2
+#define UBOX_MSX_KEY_B 0x80 // "B"
+#define UBOX_MSX_KEY_A 0x40 // "A"
+#define UBOX_MSX_KEY_FSLASH 0x10 // "/"
+#define UBOX_MSX_KEY_DOT 0x08 // "."
+#define UBOX_MSX_KEY_COMMA 0x04 // ","
+#define UBOX_MSX_KEY_QUOTE 0x02 // "'"
+#define UBOX_MSX_KEY_TICK 0x01 // "`"
+
+// row 3
+#define UBOX_MSX_KEY_J 0x80 // "J"
+#define UBOX_MSX_KEY_I 0x40 // "I"
+#define UBOX_MSX_KEY_H 0x20 // "H"
+#define UBOX_MSX_KEY_G 0x10 // "G"
+#define UBOX_MSX_KEY_F 0x08 // "F"
+#define UBOX_MSX_KEY_E 0x04 // "E"
+#define UBOX_MSX_KEY_D 0x02 // "D"
+#define UBOX_MSX_KEY_C 0x01 // "C"
+
+// row 4
+#define UBOX_MSX_KEY_R 0x80 // "R"
+#define UBOX_MSX_KEY_Q 0x40 // "Q"
+#define UBOX_MSX_KEY_P 0x20 // "P"
+#define UBOX_MSX_KEY_O 0x10 // "O"
+#define UBOX_MSX_KEY_N 0x08 // "N"
+#define UBOX_MSX_KEY_M 0x04 // "M"
+#define UBOX_MSX_KEY_L 0x02 // "L"
+#define UBOX_MSX_KEY_K 0x01 // "K"
+
+// row 5
+#define UBOX_MSX_KEY_Z 0x80 // "Z"
+#define UBOX_MSX_KEY_Y 0x40 // "Y"
+#define UBOX_MSX_KEY_X 0x20 // "X"
+#define UBOX_MSX_KEY_W 0x10 // "W"
+#define UBOX_MSX_KEY_V 0x08 // "V"
+#define UBOX_MSX_KEY_U 0x04 // "U"
+#define UBOX_MSX_KEY_T 0x02 // "T"
+#define UBOX_MSX_KEY_S 0x01 // "S"
+
+// row 6
+#define UBOX_MSX_KEY_F3 0x80 // F3
+#define UBOX_MSX_KEY_F2 0x40 // F2
+#define UBOX_MSX_KEY_F1 0x20 // F1
+#define UBOX_MSX_KEY_CODE 0x10 // CODE
+#define UBOX_MSX_KEY_CAP 0x08 // CAP
+#define UBOX_MSX_KEY_GRAPH 0x04 // GRAPH
+#define UBOX_MSX_KEY_CTRL 0x02 // CTRL
+#define UBOX_MSX_KEY_SHIFT 0x01 // SHIFT
+
+// row 7
+#define UBOX_MSX_KEY_RET 0x80 // RET
+#define UBOX_MSX_KEY_SEL 0x40 // SEL
+#define UBOX_MSX_KEY_BS 0x20 // BS
+#define UBOX_MSX_KEY_STOP 0x10 // STOP
+#define UBOX_MSX_KEY_TAB 0x08 // TAB
+#define UBOX_MSX_KEY_ESC 0x04 // ESC
+#define UBOX_MSX_KEY_F5 0x02 // F5
+#define UBOX_MSX_KEY_F4 0x01 // F4
+
+// row 8
+#define UBOX_MSX_KEY_RIGHT 0x80 // RIGHT
+#define UBOX_MSX_KEY_DOWN 0x40 // DOWN
+#define UBOX_MSX_KEY_UP 0x20 // UP
+#define UBOX_MSX_KEY_LEFT 0x10 // LEFT
+#define UBOX_MSX_KEY_DEL 0x08 // DEL
+#define UBOX_MSX_KEY_INS 0x04 // INS
+#define UBOX_MSX_KEY_HOME 0x02 // HOME
+#define UBOX_MSX_KEY_SPACE 0x01 // SPACE
+
+#endif // _UBOX_MSX_H
diff --git a/src/mplayer/Makefile b/src/mplayer/Makefile
new file mode 100644
index 0000000..81e099b
--- /dev/null
+++ b/src/mplayer/Makefile
@@ -0,0 +1,19 @@
+LIB=../../lib/mplayer.lib
+all: $(LIB)
+
+AS=sdasz80
+AR=sdar
+
+SOURCES=$(wildcard *.z80)
+OBJS=$(patsubst %.z80,%.rel,$(SOURCES))
+
+$(LIB): $(OBJS)
+ $(AR) -rcD $(LIB) $(OBJS)
+
+%.rel: %.z80
+ $(AS) -o $<
+
+.PHONY: clean
+clean:
+ rm -f $(OBJS) $(LIB)
+
diff --git a/src/mplayer/akm/LICENSE.txt b/src/mplayer/akm/LICENSE.txt
new file mode 100644
index 0000000..dd2bb6c
--- /dev/null
+++ b/src/mplayer/akm/LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016-2020 Julien Névo (contact@julien-nevo.com)
+
+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. \ No newline at end of file
diff --git a/src/mplayer/akm/Makefile b/src/mplayer/akm/Makefile
new file mode 100644
index 0000000..65da7f8
--- /dev/null
+++ b/src/mplayer/akm/Makefile
@@ -0,0 +1,4 @@
+akm_sdcc.z80: main.asm
+ rasm main.asm -o akm -s -sl -sq
+ Disark --sourceProfile sdcc --symbolFile akm.sym --src16bitsValuesInHex --src8bitsValuesInHex --undocumentedOpcodesToBytes akm.bin akm_sdcc.z80
+
diff --git a/src/mplayer/akm/PlayerAkm.asm b/src/mplayer/akm/PlayerAkm.asm
new file mode 100644
index 0000000..5a711f3
--- /dev/null
+++ b/src/mplayer/akm/PlayerAkm.asm
@@ -0,0 +1,2328 @@
+; Arkos Tracker 2 AKM (Minimalist) player (format V0).
+; By Targhan/Arkos.
+;
+; Thanks to Hicks/Vanity for two small (but relevant!) optimizations.
+
+; This compiles with RASM. Check the compatibility page on the Arkos Tracker 2 website, it contains a source converter to any Z80 assembler!;
+
+; This is a Minimalist player. Only a subset of the generic player is used. Use this player for 4k demo or other productions
+; with a tight memory limitation. However, this remains a versatile and powerful player, so it may fit any production!
+;
+; Though the player is optimized in speed, it is much slower than the generic one or the AKY player.
+; With effects used at the same time, it can reach 45 scanlines on a CPC, plus some few more if you are using sound effects.
+; So it's about as fast as the Soundtrakker 128 player, but smaller and more powerful (so what are you complaining about?).
+;
+; The player uses the stack for optimizations. Make sure the interruptions are disabled before it is called.
+; The stack pointer is saved at the beginning and restored at the end.
+;
+; Target hardware:
+; ---------------
+; This code can target Amstrad CPC, MSX, Spectrum and Pentagon. By default, it targets Amstrad CPC.
+; Simply use one of the follow line (BEFORE this player):
+; PLY_AKM_HARDWARE_CPC = 1
+; PLY_AKM_HARDWARE_MSX = 1
+; PLY_AKM_HARDWARE_SPECTRUM = 1
+; PLY_AKM_HARDWARE_PENTAGON = 1
+; Note that the PRESENCE of this variable is tested, NOT its value.
+
+; Some severe optimizations of CPU/memory can be performed:
+; ---------------------------------------------------------
+; - Use the Player Configuration of Arkos Tracker 2 to generate a configuration file to be included at the beginning of this player.
+; It will disable useless features according to your songs! Check the manual for more details, or more simply the testers.
+
+; Sound effects:
+; --------------
+; Sound effects are disabled by default. Declare PLY_AKM_MANAGE_SOUND_EFFECTS to enable it:
+; PLY_AKM_MANAGE_SOUND_EFFECTS = 1
+; Check the sound effect tester to see how it enables it.
+; Note that the PRESENCE of this variable is tested, NOT its value.
+;
+; ROM
+; ----------------------
+; To use a ROM player (no automodification, use of a small buffer to put in RAM):
+; PLY_AKM_Rom = 1
+; PLY_AKM_ROM_Buffer = #4000 (or wherever).
+; This makes the player a bit slower and slightly bigger.
+; The buffer is PLY_AKM_ROM_BufferSize bytes long (199 bytes max).
+;
+; -------------------------------------------------------
+PLY_AKM_Start:
+
+
+ ;Checks the hardware. Only one must be selected.
+PLY_AKM_HardwareCounter = 0
+ IFDEF PLY_AKM_HARDWARE_CPC
+ PLY_AKM_HardwareCounter = PLY_AKM_HardwareCounter + 1
+ ENDIF
+ IFDEF PLY_AKM_HARDWARE_MSX
+ PLY_AKM_HardwareCounter = PLY_AKM_HardwareCounter + 1
+ PLY_AKM_HARDWARE_SPECTRUM_OR_MSX = 1
+ ENDIF
+ IFDEF PLY_AKM_HARDWARE_SPECTRUM
+ PLY_AKM_HardwareCounter = PLY_AKM_HardwareCounter + 1
+ PLY_AKM_HARDWARE_SPECTRUM_OR_PENTAGON = 1
+ PLY_AKM_HARDWARE_SPECTRUM_OR_MSX = 1
+ ENDIF
+ IFDEF PLY_AKM_HARDWARE_PENTAGON
+ PLY_AKM_HardwareCounter = PLY_AKM_HardwareCounter + 1
+ PLY_AKM_HARDWARE_SPECTRUM_OR_PENTAGON = 1
+ ENDIF
+ IF PLY_AKM_HARDWARECounter > 1
+ FAIL 'Only one hardware must be selected!'
+ ENDIF
+ ;By default, selects the Amstrad CPC.
+ IF PLY_AKM_HARDWARECounter == 0
+ PLY_AKM_HARDWARE_CPC = 1
+ ENDIF
+
+
+ ;Disark macro: Word region Start.
+ disarkCounter = 0
+ IFNDEF dkws
+ MACRO dkws
+PLY_AKM_DisarkWordRegionStart_{disarkCounter}
+ ENDM
+ ENDIF
+ ;Disark macro: Word region End.
+ IFNDEF dkwe
+ MACRO dkwe
+PLY_AKM_DisarkWordRegionEnd_{disarkCounter}:
+ disarkCounter = disarkCounter + 1
+ ENDM
+ ENDIF
+
+ ;Disark macro: Pointer region Start.
+ disarkCounter = 0
+ IFNDEF dkps
+ MACRO dkps
+PLY_AKM_DisarkPointerRegionStart_{disarkCounter}
+ ENDM
+ ENDIF
+ ;Disark macro: Pointer region End.
+ IFNDEF dkpe
+ MACRO dkpe
+PLY_AKM_DisarkPointerRegionEnd_{disarkCounter}:
+ disarkCounter = disarkCounter + 1
+ ENDM
+ ENDIF
+
+ ;Disark macro: Byte region Start.
+ disarkCounter = 0
+ IFNDEF dkbs
+ MACRO dkbs
+PLY_AKM_DisarkByteRegionStart_{disarkCounter}
+ ENDM
+ ENDIF
+ ;Disark macro: Byte region End.
+ IFNDEF dkbe
+ MACRO dkbe
+PLY_AKM_DisarkByteRegionEnd_{disarkCounter}:
+ disarkCounter = disarkCounter + 1
+ ENDM
+ ENDIF
+
+ ;Disark macro: Force "No Reference Area" for 3 bytes (ld hl,xxxx).
+ IFNDEF dknr3
+ MACRO dknr3
+PLY_AKM_DisarkForceNonReferenceDuring3_{disarkCounter}:
+ disarkCounter = disarkCounter + 1
+ ENDM
+ ENDIF
+
+
+PLY_AKM_USE_HOOKS: equ 1 ;Use hooks for external calls? 0 if the Init/Play methods are directly called, will save a few bytes.
+PLY_AKM_STOP_SOUNDS: equ 1 ;1 to have the "stop sounds" code. Set it to 0 if you never plan on stopping your music.
+
+ ;Hooks for external calls. Can be removed if not needed.
+ if PLY_AKM_USE_HOOKS
+ assert PLY_AKM_Start == $ ;Makes sure no extra byte were inserted before the hooks.
+ jp PLY_AKM_Init ;Player + 0.
+ jp PLY_AKM_Play ;Player + 3.
+ if PLY_AKM_STOP_SOUNDS
+ jp PLY_AKM_Stop ;Player + 6.
+ endif
+ endif
+
+ ;Includes the sound effects player, if wanted. Important to do it as soon as possible, so that
+ ;its code can react to the Player Configuration and possibly alter it.
+ IFDEF PLY_AKM_MANAGE_SOUND_EFFECTS
+ include "PlayerAkm_SoundEffects.asm"
+ ENDIF
+ ;[[INSERT_SOUND_EFFECT_SOURCE]] ;A tag for test units. Don't touch or you're dead.
+
+ ;Is there a loaded Player Configuration source? If no, use a default configuration.
+ IFNDEF PLY_CFG_ConfigurationIsPresent
+ PLY_CFG_UseTranspositions = 1
+ PLY_CFG_UseSpeedTracks = 1
+ PLY_CFG_UseEffects = 1
+ PLY_CFG_UseHardwareSounds = 1
+ PLY_CFG_NoSoftNoHard_Noise = 1
+ PLY_CFG_SoftOnly_Noise = 1
+ PLY_CFG_SoftOnly_SoftwarePitch = 1
+ PLY_CFG_SoftToHard_SoftwarePitch = 1
+ PLY_CFG_SoftToHard_SoftwareArpeggio = 1
+ PLY_CFG_SoftAndHard_SoftwarePitch = 1
+ PLY_CFG_SoftAndHard_SoftwareArpeggio = 1
+ PLY_CFG_UseEffect_ArpeggioTable = 1
+ PLY_CFG_UseEffect_ForcePitchTableSpeed = 1
+ PLY_CFG_UseEffect_ForceArpeggioSpeed = 1
+ PLY_CFG_UseEffect_ForceInstrumentSpeed = 1
+ PLY_CFG_UseEffect_PitchUp = 1
+ PLY_CFG_UseEffect_PitchDown = 1
+ PLY_CFG_UseEffect_PitchTable = 1
+ PLY_CFG_UseEffect_SetVolume = 1
+ PLY_CFG_UseEffect_Reset = 1
+ ENDIF
+
+ ;Agglomerates some flags, because they are treated the same way by this player.
+ ;--------------------------------------------------
+ ;Creates a flag for pitch in instrument, and also pitch in hardware.
+ IFDEF PLY_CFG_SoftOnly_SoftwarePitch
+ PLY_AKM_PitchInInstrument = 1
+ ENDIF
+ IFDEF PLY_CFG_SoftToHard_SoftwarePitch
+ PLY_AKM_PitchInInstrument = 1
+ PLY_AKM_PitchInHardwareInstrument = 1
+ ENDIF
+ IFDEF PLY_CFG_SoftAndHard_SoftwarePitch
+ PLY_AKM_PitchInInstrument = 1
+ PLY_AKM_PitchInHardwareInstrument = 1
+ ENDIF
+ ;A flag for Arpeggios in Instrument, both in software and hardware.
+ IFDEF PLY_CFG_SoftOnly_SoftwareArpeggio
+ PLY_AKM_ArpeggioInSoftwareOrHardwareInstrument = 1
+ ENDIF
+ IFDEF PLY_CFG_SoftToHard_SoftwareArpeggio
+ PLY_AKM_ArpeggioInSoftwareOrHardwareInstrument = 1
+ PLY_AKM_ArpeggioInHardwareInstrument = 1
+ ENDIF
+ IFDEF PLY_CFG_SoftAndHard_SoftwareArpeggio
+ PLY_AKM_ArpeggioInSoftwareOrHardwareInstrument = 1
+ PLY_AKM_ArpeggioInHardwareInstrument = 1
+ ENDIF
+
+ ;A flag if noise is used (noise in hardware not tested, not present in this format).
+ IFDEF PLY_CFG_NoSoftNoHard_Noise
+ PLY_AKM_USE_Noise = 1
+ ENDIF
+ IFDEF PLY_CFG_SoftOnly_Noise
+ PLY_AKM_USE_Noise = 1
+ ENDIF
+ ;The noise is managed? Then the noise register access must be compiled.
+ IFDEF PLY_AKM_USE_Noise
+ PLY_AKM_USE_NoiseRegister = 1
+ ENDIF
+
+ ;Mixing Pitch up/down effects.
+ IFDEF PLY_CFG_UseEffect_PitchUp
+ PLY_AKM_USE_EffectPitchUpDown = 1
+ ENDIF
+ IFDEF PLY_CFG_UseEffect_PitchDown
+ PLY_AKM_USE_EffectPitchUpDown = 1
+ ENDIF
+
+ ;If the Force Arpeggio Speed if used, it means the ArpeggioTable effect must also be!
+ IFDEF PLY_CFG_UseEffect_ForceArpeggioSpeed
+ PLY_CFG_UseEffect_ArpeggioTable = 1
+ ENDIF
+ ;If the Force Pitch Table Speed if used, it means the PitchTable effect must also be!
+ IFDEF PLY_CFG_UseEffect_ForcePitchTableSpeed
+ PLY_CFG_UseEffect_PitchTable = 1
+ ENDIF
+
+;A nice trick to manage the offset using the same instructions, according to the player (ROM or not).
+ IFDEF PLY_AKM_Rom
+PLY_AKM_Offset1b: equ 0
+PLY_AKM_Offset2b: equ 0 ;Used for instructions such as ld iyh,xx
+ ELSE
+PLY_AKM_Offset1b: equ 1
+PLY_AKM_Offset2b: equ 2
+ ENDIF
+
+;Initializes the song. MUST be called before actually playing the song.
+;IN: HL = Address of the song.
+; A = Index of the subsong to play (>=0).
+PLY_AKM_InitDisarkGenerateExternalLabel:
+PLY_AKM_Init:
+ ;Reads the Song header.
+ ;Reads the pointers to the various index tables.
+ ld de,PLY_AKM_PtInstruments + PLY_AKM_Offset1b
+ ldi
+ ldi
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+ ld de,PLY_AKM_PtArpeggios + PLY_AKM_Offset1b
+ ldi
+ ldi
+ ELSE
+ inc hl
+ inc hl
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+ ld de,PLY_AKM_PtPitches + PLY_AKM_Offset1b
+ ldi
+ ldi
+ ELSE
+ inc hl
+ inc hl
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+ ELSE
+dknr3 (void): ld de,4
+ add hl,de
+ ENDIF ;PLY_CFG_UseEffects
+
+ ;Finds the address of the Subsong.
+ ;HL points on the table, adds A * 2.
+ ;Possible optimization: possible to set the Subsong directly.
+ add a,a
+ ld e,a
+ ld d,0
+ add hl,de
+ ld a,(hl)
+ inc hl
+ ld h,(hl)
+ ld l,a
+
+ ;Reads the header of the Subsong, copies the values inside the code via a table.
+ ld ix,PLY_AKM_InitVars_Start
+ ld a,(PLY_AKM_InitVars_End - PLY_AKM_InitVars_Start) / 2
+PLY_AKM_InitVars_Loop:
+ ld e,(ix + 0)
+ ld d,(ix + 1)
+ inc ix
+ inc ix
+ ldi
+ dec a
+ jr nz,PLY_AKM_InitVars_Loop
+
+ ;A is zero, no need to reset it.
+ ld (PLY_AKM_PatternRemainingHeight + PLY_AKM_Offset1b),a ;Optimization: this line can be removed if there is no need to reset the song (warning, A is used below).
+
+ ;Stores the Linker address, just after.
+ ex de,hl
+ ld hl,PLY_AKM_PtLinker + PLY_AKM_Offset1b
+ ld (hl),e
+ inc hl
+ ld (hl),d
+
+ ;A big LDIR to erase all the data blocks. Optimization: can be removed if there is no need to reset the song.
+ ;A is considered 0!
+ ld hl,PLY_AKM_Track1_Data
+ ld de,PLY_AKM_Track1_Data + 1
+dknr3 (void): ld bc,PLY_AKM_Track3_Data_End - PLY_AKM_Track1_Data - 1
+ ld (hl),a
+ ldir
+
+ ;Resets this flag. Especially important for ROM.
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+ ld (PLY_AKM_RT_ReadEffectsFlag + PLY_AKM_Offset1b),a
+ ENDIF
+
+ ;Forces a new line.
+ ld a,(PLY_AKM_Speed + PLY_AKM_Offset1b)
+ dec a
+ ld (PLY_AKM_TickCounter + PLY_AKM_Offset1b),a
+
+ ;Reads the first instrument, the empty one, and set-ups the pointers to the instrument to read.
+ ;Optimization: needed if the song doesn't start with an instrument on all the channels. Else, it can be removed.
+ ld hl,(PLY_AKM_PtInstruments + PLY_AKM_Offset1b)
+ ld e,(hl)
+ inc hl
+ ld d,(hl)
+ inc de ;Skips the header.
+ ld (PLY_AKM_Track1_PtInstrument),de
+ ld (PLY_AKM_Track2_PtInstrument),de
+ ld (PLY_AKM_Track3_PtInstrument),de
+
+ ;If sound effects, clears the SFX state.
+ IFDEF PLY_AKM_MANAGE_SOUND_EFFECTS
+dknr3 (void): ld hl,0
+ ld (PLY_AKM_Channel1_SoundEffectData),hl
+ ld (PLY_AKM_Channel2_SoundEffectData),hl
+ ld (PLY_AKM_Channel3_SoundEffectData),hl
+ ENDIF ;PLY_AKM_MANAGE_SOUND_EFFECTS
+
+ ;For ROM, generates the RET table.
+ IFDEF PLY_AKM_Rom
+ ld ix,PLY_AKM_RegistersForRom ;Source.
+ ld iy,PLY_AKM_Registers_RetTable ;Destination.
+ ld bc,PLY_AKM_SendPsgRegister
+dknr3 (void): ld de,4
+PLY_AKM_InitRom_Loop:
+ ld a,(ix) ;Gets the register.
+ ld h,a
+ inc ix
+ and %00111111
+ ld (iy + 0),a ;Writes the register.
+ ld (iy + 1),0 ;Value is 0 for now.
+ ld a,h
+ and %11000000
+ jr nz,PLY_AKM_InitRom_Special
+ ;Encodes the "normal" SendPsgRegister code address.
+ ld (iy + 2),c
+ ld (iy + 3),b
+ add iy,de
+ jr PLY_AKM_InitRom_Loop
+PLY_AKM_InitRom_Special:
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+ rl h
+ jr c,PLY_AKM_InitRom_WriteEndCode
+ ;Bit 6 must be set if we came here.
+ ld bc,PLY_AKM_SendPsgRegisterR13
+ ld (iy + 2),c
+ ld (iy + 3),b
+ ld bc,PLY_AKM_SendPsgRegisterAfterPop ;This one is a trick to send the register after R13 is managed.
+ ld (iy + 4),c
+ ld (iy + 5),b
+ add iy,de ;Only advance of 4, the code belows expects that.
+ ENDIF ;PLY_CFG_UseHardwareSounds
+
+PLY_AKM_InitRom_WriteEndCode:
+ ld bc,PLY_AKM_SendPsgRegisterEnd
+ ld (iy + 2),c
+ ld (iy + 3),b
+ ENDIF
+ ret
+
+ ;If ROM, the registers to send, IN THE ORDER they are declared in the ROM buffer!
+ ;Bit 7 if ends (end DW to encode). Exclusive to bit 6.
+ ;Bit 6 if R13/AfterPop the end DW to encode. Exclusive to bit 7.
+ IFDEF PLY_AKM_Rom
+PLY_AKM_RegistersForRom:
+dkbs (void):
+ db 8, 0, 1, 9, 2, 3, 10, 4, 5
+ IFDEF PLY_AKM_USE_NoiseRegister ;CONFIG SPECIFIC
+ db 6
+ ENDIF
+ IFNDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+ db 7 + 128
+ ELSE
+ db 7, 11, 12 + 64 ;13 is NOT declared, special case.
+ ENDIF
+dkbe (void):
+ ENDIF
+
+;Addresses where to put the header data.
+PLY_AKM_InitVars_Start:
+dkps (void):
+ dw PLY_AKM_NoteIndexTable + PLY_AKM_Offset1b
+ dw PLY_AKM_NoteIndexTable + PLY_AKM_Offset1b + 1
+ dw PLY_AKM_TrackIndex + PLY_AKM_Offset1b
+ dw PLY_AKM_TrackIndex + PLY_AKM_Offset1b + 1
+ dw PLY_AKM_Speed + PLY_AKM_Offset1b
+ dw PLY_AKM_PrimaryInstrument + PLY_AKM_Offset1b
+ dw PLY_AKM_SecondaryInstrument + PLY_AKM_Offset1b
+ dw PLY_AKM_PrimaryWait + PLY_AKM_Offset1b
+ dw PLY_AKM_SecondaryWait + PLY_AKM_Offset1b
+ dw PLY_AKM_DefaultStartNoteInTracks + PLY_AKM_Offset1b
+ dw PLY_AKM_DefaultStartInstrumentInTracks + PLY_AKM_Offset1b
+ dw PLY_AKM_DefaultStartWaitInTracks + PLY_AKM_Offset1b
+ dw PLY_AKM_FlagNoteAndEffectInCell + PLY_AKM_Offset1b
+dkpe (void):
+PLY_AKM_InitVars_End:
+
+
+;Cuts the channels, stopping all sounds.
+ if PLY_AKM_STOP_SOUNDS
+PLY_AKM_StopDisarkGenerateExternalLabel:
+PLY_AKM_Stop:
+ ld (PLY_AKM_SaveSP + PLY_AKM_Offset1b),sp
+
+ xor a
+ ld (PLY_AKM_Track1_Volume),a
+ ld (PLY_AKM_Track2_Volume),a
+ ld (PLY_AKM_Track3_Volume),a
+ IFDEF PLY_AKM_HARDWARE_MSX
+ ld a,%10111111 ;On MSX, bit 7 must be 1, bit 6 0.
+ ELSE
+ ld a,%00111111 ;On CPC, bit 6 must be 0. Other platforms don't care.
+ ENDIF
+ ld (PLY_AKM_MixerRegister),a
+ jp PLY_AKM_SendPsg
+ endif ;PLY_AKM_STOP_SOUNDS
+
+
+
+;Plays one frame of the song. It MUST have been initialized before.
+;The stack is saved and restored, but is diverted, so watch out for the interruptions.
+PLY_AKM_PlayDisarkGenerateExternalLabel:
+PLY_AKM_Play:
+ ld (PLY_AKM_SaveSP + PLY_AKM_Offset1b),sp
+
+ ;Reads a new line?
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_TickCounter: ld a,0
+ inc a
+PLY_AKM_Speed: cp 1 ;Speed (>0).
+ ELSE
+ ld a,(PLY_AKM_Speed)
+ ld b,a
+ ld a,(PLY_AKM_TickCounter)
+ inc a
+ cp b
+ ENDIF
+ jp nz,PLY_AKM_TickCounterManaged
+
+ ;A new line must be read. But have we reached the end of the Pattern?
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_PatternRemainingHeight: ld a,0 ;Height. If 0, end of the pattern.
+ ELSE
+ ld a,(PLY_AKM_PatternRemainingHeight)
+ ENDIF
+ sub 1
+ jr c,PLY_AKM_Linker
+ ;Pattern not ended. No need to read the Linker.
+ ld (PLY_AKM_PatternRemainingHeight + PLY_AKM_Offset1b),a
+ jr PLY_AKM_ReadLine
+
+ ;New pattern. Reads the Linker.
+PLY_AKM_Linker:
+ IFNDEF PLY_AKM_Rom
+dknr3 (void):
+PLY_AKM_TrackIndex: ld de,0 ;DE' points on the Track Index. Useful when new Tracks are found.
+ ELSE
+ ld de,(PLY_AKM_TrackIndex)
+ ENDIF
+ exx
+ IFNDEF PLY_AKM_Rom
+dknr3 (void):
+PLY_AKM_PtLinker: ld hl,0
+ ELSE
+ ld hl,(PLY_AKM_PtLinker)
+ ENDIF
+PLY_AKM_LinkerPostPt:
+ ;Resets the possible empty cell counter of each Track.
+ xor a
+ ld (PLY_AKM_Track1_WaitEmptyCell),a
+ ld (PLY_AKM_Track2_WaitEmptyCell),a
+ ld (PLY_AKM_Track3_WaitEmptyCell),a
+ ;On new pattern, the escape note/instrument/wait values are set for each Tracks.
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_DefaultStartNoteInTracks: ld a,0
+ ELSE
+ ld a,(PLY_AKM_DefaultStartNoteInTracks)
+ ENDIF
+ ld (PLY_AKM_Track1_EscapeNote),a
+ ld (PLY_AKM_Track2_EscapeNote),a
+ ld (PLY_AKM_Track3_EscapeNote),a
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_DefaultStartInstrumentInTracks: ld a,0
+ ELSE
+ ld a,(PLY_AKM_DefaultStartInstrumentInTracks)
+ ENDIF
+ ld (PLY_AKM_Track1_EscapeInstrument),a
+ ld (PLY_AKM_Track2_EscapeInstrument),a
+ ld (PLY_AKM_Track3_EscapeInstrument),a
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_DefaultStartWaitInTracks: ld a,0
+ ELSE
+ ld a,(PLY_AKM_DefaultStartWaitInTracks)
+ ENDIF
+ ld (PLY_AKM_Track1_EscapeWait),a
+ ld (PLY_AKM_Track2_EscapeWait),a
+ ld (PLY_AKM_Track3_EscapeWait),a
+
+ ;Reads the state byte of the pattern.
+ ld b,(hl)
+ inc hl
+ rr b ;Speed change or end of song?
+ jr nc,PLY_AKM_LinkerAfterSpeedChange
+ ;Next byte is either the speed (>0) or an end of song marker.
+ ld a,(hl)
+ inc hl
+ ;If no speed used, it means "end of song" every time.
+ IFDEF PLY_CFG_UseSpeedTracks ;CONFIG SPECIFIC
+ or a ;0 if end of song, else speed.
+ jr nz,PLY_AKM_LinkerSpeedChange
+ ENDIF ;PLY_CFG_UseSpeedTracks
+ ;End of song.
+ ld a,(hl) ;Reads where to loop in the Linker.
+ inc hl
+ ld h,(hl)
+ ld l,a
+ jr PLY_AKM_LinkerPostPt
+ IFDEF PLY_CFG_UseSpeedTracks ;CONFIG SPECIFIC
+PLY_AKM_LinkerSpeedChange:
+ ;Speed change.
+ ld (PLY_AKM_Speed + PLY_AKM_Offset1b),a
+ ENDIF ;PLY_CFG_UseSpeedTracks
+PLY_AKM_LinkerAfterSpeedChange:
+
+ ;New height?
+ rr b
+ jr nc,PLY_AKM_LinkerUsePreviousHeight
+ ld a,(hl)
+ inc hl
+ ld (PLY_AKM_LinkerPreviousRemainingHeight + PLY_AKM_Offset1b),a
+ jr PLY_AKM_LinkerSetRemainingHeight
+ ;The same height is used. It was stored before.
+PLY_AKM_LinkerUsePreviousHeight:
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_LinkerPreviousRemainingHeight: ld a,0
+ ELSE
+ ld a,(PLY_AKM_LinkerPreviousRemainingHeight)
+ ENDIF
+PLY_AKM_LinkerSetRemainingHeight:
+ ld (PLY_AKM_PatternRemainingHeight + PLY_AKM_Offset1b),a
+
+ ;New Transposition and Track for channel 1?
+ ld ix,PLY_AKM_Track1_Data
+ call PLY_AKM_CheckTranspositionAndTrack
+ ;New Transposition and Track for channel 2?
+ ld ix,PLY_AKM_Track2_Data
+ call PLY_AKM_CheckTranspositionAndTrack
+ ;New Transposition and Track for channel 3?
+ ld ix,PLY_AKM_Track3_Data
+ call PLY_AKM_CheckTranspositionAndTrack
+
+ ld (PLY_AKM_PtLinker + PLY_AKM_Offset1b),hl
+
+
+;Reads the Tracks.
+;---------------------------------
+PLY_AKM_ReadLine:
+ IFNDEF PLY_AKM_Rom
+dknr3 (void):
+PLY_AKM_PtInstruments: ld de,0
+dknr3 (void):
+PLY_AKM_NoteIndexTable: ld bc,0
+ ELSE
+ ld de,(PLY_AKM_PtInstruments)
+ ld bc,(PLY_AKM_NoteIndexTable)
+ ENDIF
+ exx
+ ld ix,PLY_AKM_Track1_Data
+ call PLY_AKM_ReadTrack
+ ld ix,PLY_AKM_Track2_Data
+ call PLY_AKM_ReadTrack
+ ld ix,PLY_AKM_Track3_Data
+ call PLY_AKM_ReadTrack
+
+ xor a
+PLY_AKM_TickCounterManaged:
+ ld (PLY_AKM_TickCounter + PLY_AKM_Offset1b),a
+
+
+
+;Plays the sound stream.
+;---------------------------------
+ ld de,PLY_AKM_PeriodTable
+ exx
+
+ ld c,%11100000 ;Register 7, shifted of 2 to the left. Bits 2 and 5 will be possibly changed by each iteration.
+
+ ld ix,PLY_AKM_Track1_Data
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+ call PLY_AKM_ManageEffects
+ ENDIF ;PLY_CFG_UseEffects
+ ld iy,PLY_AKM_Track1_Registers
+ call PLY_AKM_PlaySoundStream
+
+ srl c ;Not RR, because we have to make sure the b6 is 0, else no more keyboard (on CPC)!
+ ;Also, on MSX, bit 6 must be 0.
+ ld ix,PLY_AKM_Track2_Data
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+ call PLY_AKM_ManageEffects
+ ENDIF ;PLY_CFG_UseEffects
+ ld iy,PLY_AKM_Track2_Registers
+ call PLY_AKM_PlaySoundStream
+
+ IFDEF PLY_AKM_HARDWARE_MSX
+ scf ;On MSX, bit 7 must be 1.
+ rr c
+ ELSE
+ rr c ;On other platforms, we don't care about b7.
+ ENDIF
+ ld ix,PLY_AKM_Track3_Data
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+ call PLY_AKM_ManageEffects
+ ENDIF ;PLY_CFG_UseEffects
+ ld iy,PLY_AKM_Track3_Registers
+ call PLY_AKM_PlaySoundStream
+
+ ld a,c
+
+;Plays the sound effects, if desired.
+;-------------------------------------------
+ IFDEF PLY_AKM_MANAGE_SOUND_EFFECTS
+ call PLY_AKM_PlaySoundEffectsStream
+ ELSE
+ ld (PLY_AKM_MixerRegister),a
+ ENDIF ;PLY_AKM_MANAGE_SOUND_EFFECTS
+
+
+
+;Sends the values to the PSG.
+;---------------------------------
+PLY_AKM_SendPsg:
+ ld sp,PLY_AKM_Registers_RetTable
+
+ IFDEF PLY_AKM_HARDWARE_CPC
+dknr3 (void): ld bc,#f680
+ ld a,#c0
+dknr3 (void): ld de,#f4f6
+ out (c),a ;#f6c0 ;Madram's trick requires to start with this. out (c),b works, but will activate K7's relay! Not clean.
+ ENDIF
+
+ IFDEF PLY_AKM_HARDWARE_SPECTRUM_OR_PENTAGON
+dknr3 (void): ld de,#bfff
+ ld c,#fd
+ ENDIF
+
+PLY_AKM_SendPsgRegister:
+ pop hl ;H = value, L = register.
+PLY_AKM_SendPsgRegisterAfterPop:
+ IFDEF PLY_AKM_HARDWARE_CPC
+ ld b,d
+ out (c),l ;#f400 + register.
+ ld b,e
+ out (c),0 ;#f600
+ ld b,d
+ out (c),h ;#f400 + value.
+ ld b,e
+ out (c),c ;#f680
+ out (c),a ;#f6c0
+ ENDIF
+
+ IFDEF PLY_AKM_HARDWARE_SPECTRUM_OR_PENTAGON
+ ld b,e
+ out (c),l ;#fffd + register.
+ ld b,d
+ out (c),h ;#bffd + value
+ ENDIF
+
+ IFDEF PLY_AKM_HARDWARE_MSX
+ ld a,l ;Register.
+ out (#a0),a
+ ld a,h ;Value.
+ out (#a1),a
+ ENDIF
+ ret
+
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+PLY_AKM_SendPsgRegisterR13:
+
+ ;Should the R13 be played? Yes only if different. No "force retrig" is managed by this player.
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_SetReg13: ld a,0
+PLY_AKM_SetReg13Old: cp 0
+ ELSE
+ ld a,(PLY_AKM_SetReg13Old)
+ ld b,a
+ ld a,(PLY_AKM_SetReg13)
+ cp b
+ ENDIF
+ jr z,PLY_AKM_SendPsgRegisterEnd
+ ;Different. R13 must be played. Updates the old R13 value.
+ ld (PLY_AKM_SetReg13Old + PLY_AKM_Offset1b),a
+
+ ld h,a
+ ld l,13
+
+ IFDEF PLY_AKM_HARDWARE_CPC
+ ld a,#c0
+ ENDIF
+
+ ret ;Sends the 13th registers.
+ ENDIF ;PLY_CFG_UseHardwareSounds
+PLY_AKM_SendPsgRegisterEnd:
+
+ IFNDEF PLY_AKM_Rom
+dknr3 (void):
+PLY_AKM_SaveSP: ld sp,0
+ ELSE
+ ld sp,(PLY_AKM_SaveSP)
+ ENDIF
+ ret
+
+
+
+
+
+;Shifts B to the right, if carry, a transposition is read.
+;Shifts B to the right once again, if carry, a new Track is read (may be an index or a track offset).
+;IN: HL = where to read the data.
+; IX = points on the track data buffer.
+; DE'= Track index table
+; B = flags.
+;OUT: B = shifted of two.
+; HL = increased according to read data.
+PLY_AKM_CheckTranspositionAndTrack:
+ ;New transposition?
+ rr b
+ IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC
+ jr nc,PLY_AKM_CheckTranspositionAndTrack_AfterTransposition
+ ;Transposition.
+ ld a,(hl)
+ ld (ix + PLY_AKM_Data_OffsetTransposition),a
+ inc hl
+PLY_AKM_CheckTranspositionAndTrack_AfterTransposition:
+ ENDIF ;PLY_CFG_UseTranspositions
+ ;New Track?
+ rr b
+ jr nc,PLY_AKM_CheckTranspositionAndTrack_NoNewTrack
+ ;New Track.
+ ld a,(hl)
+ inc hl
+ ;Is it a reference?
+ sla a
+ jr nc,PLY_AKM_CheckTranspositionAndTrack_TrackOffset
+ ;Reference.
+ exx
+ ld l,a ;A is the track index * 2.
+ ld h,0
+ add hl,de ;HL points on the track address.
+ ld a,(hl)
+ ld (ix + PLY_AKM_Data_OffsetPtStartTrack + 0),a
+ ld (ix + PLY_AKM_Data_OffsetPtTrack + 0),a
+ inc hl
+ ld a,(hl)
+ ld (ix + PLY_AKM_Data_OffsetPtStartTrack + 1),a
+ ld (ix + PLY_AKM_Data_OffsetPtTrack + 1),a
+ exx
+ ret
+PLY_AKM_CheckTranspositionAndTrack_TrackOffset:
+ ;The Track is an offset. Counter the previous shift.
+ rra ;Carry was 0, so bit 7 is 0.
+ ld d,a ;D is the MSB of the offset.
+ ld e,(hl) ;Reads the LSB of the offset.
+ inc hl
+
+ ld c,l ;Saves HL.
+ ld a,h
+
+ add hl,de ;HL is now the Track (offset + $ (past offset));
+ ld (ix + PLY_AKM_Data_OffsetPtStartTrack + 0),l
+ ld (ix + PLY_AKM_Data_OffsetPtStartTrack + 1),h
+ ld (ix + PLY_AKM_Data_OffsetPtTrack + 0),l
+ ld (ix + PLY_AKM_Data_OffsetPtTrack + 1),h
+
+ ld l,c ;Retrieves HL.
+ ld h,a
+ ret
+PLY_AKM_CheckTranspositionAndTrack_NoNewTrack:
+ ;Copies the old Track inside the new Track pointer, as it evolves.
+ ld a,(ix + PLY_AKM_Data_OffsetPtStartTrack + 0)
+ ld (ix + PLY_AKM_Data_OffsetPtTrack + 0),a
+ ld a,(ix + PLY_AKM_Data_OffsetPtStartTrack + 1)
+ ld (ix + PLY_AKM_Data_OffsetPtTrack + 1),a
+ ret
+
+
+
+
+
+;Reads a Track.
+;IN: IX = Data block of the Track.
+; DE'= Instrument table. Do not modify!
+; BC'= Note index table. Do not modify!
+PLY_AKM_ReadTrack:
+ ;Are there any empty lines to wait?
+ ld a,(ix + PLY_AKM_Data_OffsetWaitEmptyCell)
+ sub 1
+ jr c,PLY_AKM_RT_NoEmptyCell
+ ;Wait!
+ ld (ix + PLY_AKM_Data_OffsetWaitEmptyCell),a
+ ret
+
+PLY_AKM_RT_NoEmptyCell:
+ ;Reads the Track pointer.
+ ld l,(ix + PLY_AKM_Data_OffsetPtTrack + 0)
+ ld h,(ix + PLY_AKM_Data_OffsetPtTrack + 1)
+PLY_AKM_RT_GetDataByte:
+ ld b,(hl)
+ inc hl
+ ;First, reads the note/effect flag.
+ IFDEF PLY_AKM_Rom
+ ld a,(PLY_AKM_FlagNoteAndEffectInCell)
+ ld c,a
+ ENDIF
+ ld a,b
+ and %1111 ;Keeps only the note/data.
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_FlagNoteAndEffectInCell: cp 12 ;0-12 = note reference if no effects in the song, or 0-11 if there are effects in the song.
+ ELSE
+ cp c
+ ENDIF
+ jr c,PLY_AKM_RT_NoteReference
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+ sub 12 ;Can not be optimized with the code above, its value is automodified.
+ jr z,PLY_AKM_RT_NoteAndEffects
+ dec a
+ jr z,PLY_AKM_RT_NoNoteMaybeEffects
+ ELSE
+ sub 13
+ jr z,PLY_AKM_RT_ReadWaitFlags ;If no effects, directly check the wait flag.
+ ENDIF ;PLY_CFG_UseEffects
+ dec a
+ jr z,PLY_AKM_RT_NewEscapeNote
+ ;15. Same escape note.
+ ld a,(ix + PLY_AKM_Data_OffsetEscapeNote)
+ jr PLY_AKM_RT_AfterNoteRead
+
+PLY_AKM_RT_NewEscapeNote:
+ ;Reads the escape note, and stores it, it may be reused by other cells.
+ ld a,(hl)
+ ld (ix + PLY_AKM_Data_OffsetEscapeNote),a
+ inc hl
+ jr PLY_AKM_RT_AfterNoteRead
+
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+PLY_AKM_RT_NoteAndEffects
+ ;There is a "note and effects". This is a special case. A new data byte must be read, with the note and the normal flags.
+ ;However, we use a "force effects" to mark the presence of effects.
+ dec a ;A is 0, give it any other value.
+ ld (PLY_AKM_RT_ReadEffectsFlag + PLY_AKM_Offset1b),a
+ jr PLY_AKM_RT_GetDataByte
+
+PLY_AKM_RT_NoNoteMaybeEffects
+ ;Reads flag "instrument" to know what to do. The flags are diverted to indicate whether there are effects.
+ bit 4,b ;Effects?
+ jr z,PLY_AKM_RT_ReadWaitFlags ;No effects. As there is no note, logically, there are no instrument to read, so simply reads the Wait value.
+ ld a,b ;B is not 0, so it works.
+ ld (PLY_AKM_RT_ReadEffectsFlag + PLY_AKM_Offset1b),a
+ jr PLY_AKM_RT_ReadWaitFlags
+ ENDIF ;PLY_CFG_UseEffects
+
+PLY_AKM_RT_NoteReference:
+ ;A is the index of the note.
+ exx
+ ld l,a
+ ld h,0
+ add hl,bc
+ ld a,(hl)
+ exx
+
+ ;A is the right note (0-127).
+PLY_AKM_RT_AfterNoteRead:
+ ;Adds the transposition.
+ IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC
+ add a,(ix + PLY_AKM_Data_OffsetTransposition)
+ ENDIF ;PLY_CFG_UseTranspositions
+ ld (ix + PLY_AKM_Data_OffsetBaseNote),a
+
+ ;Reads the instruments flags.
+ ;------------------
+ ld a,b
+ and %110000
+ jr z,PLY_AKM_RT_SameEscapeInstrument
+ cp %010000
+ jr z,PLY_AKM_RT_PrimaryInstrument
+ cp %100000
+ jr z,PLY_AKM_RT_SecondaryInstrument
+ ;New escape instrument. Reads and stores it, it may be reused by other cells.
+ ld a,(hl)
+ inc hl
+ ld (ix + PLY_AKM_Data_OffsetEscapeInstrument),a
+ jr PLY_AKM_RT_StoreCurrentInstrument
+
+PLY_AKM_RT_SameEscapeInstrument:
+ ;Use the latest escape instrument.
+ ld a,(ix + PLY_AKM_Data_OffsetEscapeInstrument)
+ jr PLY_AKM_RT_StoreCurrentInstrument
+
+PLY_AKM_RT_SecondaryInstrument:
+ ;Use the secondary instrument.
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_SecondaryInstrument: ld a,0
+ ELSE
+ ld a,(PLY_AKM_SecondaryInstrument)
+ ENDIF
+ jr PLY_AKM_RT_StoreCurrentInstrument
+
+PLY_AKM_RT_PrimaryInstrument:
+ ;Use the primary instrument.
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_PrimaryInstrument: ld a,0
+ ELSE
+ ld a,(PLY_AKM_PrimaryInstrument)
+ ENDIF
+
+PLY_AKM_RT_StoreCurrentInstrument:
+ ;A is the instrument to play.
+ exx
+ ;Gets the address of the Instrument.
+ add a,a ;Only 127 instruments max.
+ ld l,a
+ ld h,0
+ add hl,de ;Adds to the Instrument Table.
+ ld a,(hl)
+ inc hl
+ ld h,(hl)
+ ld l,a
+ ;Reads the header of the Instrument.
+ ld a,(hl) ;Speed.
+ inc hl
+ ld (ix + PLY_AKM_Data_OffsetInstrumentSpeed),a
+ ;Stores the pointer on the data of the Instrument.
+ ld (ix + PLY_AKM_Data_OffsetPtInstrument + 0),l
+ ld (ix + PLY_AKM_Data_OffsetPtInstrument + 1),h
+ exx
+ xor a
+ ;Resets the step on the Instrument.
+ ld (ix + PLY_AKM_Data_OffsetInstrumentCurrentStep),a
+ ;Resets the Track pitch.
+ IFDEF PLY_AKM_USE_EffectPitchUpDown ;CONFIG SPECIFIC
+ ld (ix + PLY_AKM_Data_OffsetIsPitchUpDownUsed),a
+ ld (ix + PLY_AKM_Data_OffsetTrackPitchInteger + 0),a
+ ld (ix + PLY_AKM_Data_OffsetTrackPitchInteger + 1),a
+ ;ld (ix + PLY_AKM_Data_OffsetTrackPitchDecimal),a ;Shouldn't be needed, the difference shouldn't be noticeable.
+ ENDIF ;PLY_AKM_USE_EffectPitchUpDown
+
+ ;Resets the offset on Arpeggio and Pitch tables.
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+ ld (ix + PLY_AKM_Data_OffsetPtArpeggioOffset),a
+ ld (ix + PLY_AKM_Data_OffsetArpeggioCurrentStep),a
+ IFDEF PLY_CFG_UseEffect_ForceArpeggioSpeed ;CONFIG SPECIFIC
+ ld a,(ix + PLY_AKM_Data_OffsetArpeggioOriginalSpeed) ;The arpeggio speed must be reset.
+ ld (ix + PLY_AKM_Data_OffsetArpeggioCurrentSpeed),a
+ ENDIF ;PLY_CFG_UseEffect_ForceArpeggioSpeed
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+ ld (ix + PLY_AKM_Data_OffsetPtPitchOffset),a
+ ld (ix + PLY_AKM_Data_OffsetPitchCurrentStep),a
+ IFDEF PLY_CFG_UseEffect_ForcePitchTableSpeed ;CONFIG SPECIFIC
+ ld a,(ix + PLY_AKM_Data_OffsetPitchOriginalSpeed) ;The pitch speed must be reset.
+ ld (ix + PLY_AKM_Data_OffsetPitchCurrentSpeed),a
+ ENDIF ;PLY_CFG_UseEffect_ForcePitchTableSpeed
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+
+
+ ;Reads the wait flags.
+ ;----------------------
+PLY_AKM_RT_ReadWaitFlags:
+ ld a,b
+ and %11000000
+ jr z,PLY_AKM_RT_SameEscapeWait
+ cp %01000000
+ jr z,PLY_AKM_RT_PrimaryWait
+ cp %10000000
+ jr z,PLY_AKM_RT_SecondaryWait
+ ;New escape wait. Reads and stores it, it may be reused by other cells.
+ ld a,(hl)
+ inc hl
+ ld (ix + PLY_AKM_Data_OffsetEscapeWait),a
+ jr PLY_AKM_RT_StoreCurrentWait
+
+PLY_AKM_RT_SameEscapeWait:
+ ;Use the latest escape wait.
+ ld a,(ix + PLY_AKM_Data_OffsetEscapeWait)
+ jr PLY_AKM_RT_StoreCurrentWait
+
+PLY_AKM_RT_PrimaryWait:
+ ;Use the primary wait.
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_PrimaryWait: ld a,0
+ ELSE
+ ld a,(PLY_AKM_PrimaryWait)
+ ENDIF
+ jr PLY_AKM_RT_StoreCurrentWait
+
+PLY_AKM_RT_SecondaryWait:
+ ;Use the secondary wait.
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_SecondaryWait: ld a,0
+ ELSE
+ ld a,(PLY_AKM_SecondaryWait)
+ ENDIF
+
+PLY_AKM_RT_StoreCurrentWait:
+ ;A is the wait to store.
+ ld (ix + PLY_AKM_Data_OffsetWaitEmptyCell),a
+
+ ;--------------------
+ ;Are there effects to read?
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_RT_ReadEffectsFlag: ld a,0
+ ELSE
+ ld a,(PLY_AKM_RT_ReadEffectsFlag)
+ ENDIF
+ or a
+ jr nz,PLY_AKM_RT_ReadEffects
+PLY_AKM_RT_AfterEffects:
+ ENDIF ;PLY_CFG_UseEffects
+ ;No effects, or after they have been managed.
+ ;Saves the new pointer on the Track.
+ ld (ix + PLY_AKM_Data_OffsetPtTrack + 0),l
+ ld (ix + PLY_AKM_Data_OffsetPtTrack + 1),h
+ ret
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+PLY_AKM_RT_ReadEffects:
+ ;Resets the effect presence flag.
+ xor a
+ ld (PLY_AKM_RT_ReadEffectsFlag + PLY_AKM_Offset1b),a
+
+PLY_AKM_RT_ReadEffect:
+ ld iy,PLY_AKM_EffectTable
+ ;Reads effect number and possible data. All effect must jump to PLY_AKM_RT_ReadEffect_Return when finished.
+ ld b,(hl)
+ ld a,b
+ inc hl
+
+ and %1110
+ ld e,a
+ ld d,0
+ add iy,de
+
+ ;As a convenience, puts the effect nibble "to the right", for direct use.
+ ld a,b
+ rra
+ rra
+ rra
+ rra
+ and %1111 ;This sets the carry flag, useful for the effects code.
+ ;Executes the effect code.
+ jp (iy)
+PLY_AKM_RT_ReadEffect_Return:
+ ;More effects?
+ bit 0,b
+ jr nz,PLY_AKM_RT_ReadEffect
+ jr PLY_AKM_RT_AfterEffects
+
+PLY_AKM_RT_WaitLong:
+ ;A 8-bit byte is encoded just after.
+ ld a,(hl)
+ inc hl
+ ld (ix + PLY_AKM_Data_OffsetWaitEmptyCell),a
+ jr PLY_AKM_RT_CellRead
+PLY_AKM_RT_WaitShort:
+ ;Only a 2-bit value is encoded.
+ ld a,b
+ rlca ;Transfers the bit 7/6 to 1/0. Thanks Hicks for the RCLA trick!
+ rlca
+ and %11
+ ld (ix + PLY_AKM_Data_OffsetWaitEmptyCell),a
+ ;jr PLY_AKM_RT_CellRead
+;Jumped to after the Cell has been read.
+;IN: HL = new value of the Track pointer. Must point after the read Cell.
+PLY_AKM_RT_CellRead:
+ ld (ix + PLY_AKM_Data_OffsetPtTrack + 0),l
+ ld (ix + PLY_AKM_Data_OffsetPtTrack + 1),h
+ ret
+
+
+;Manages the effects, if any. For the activated effects, modifies the internal data for the Track which data block is given.
+;IN: IX = data block of the Track.
+;OUT: IX, IY = unmodified.
+; C must NOT be modified!
+; DE' must NOT be modified!
+PLY_AKM_ManageEffects:
+ IFDEF PLY_AKM_USE_EffectPitchUpDown ;CONFIG SPECIFIC
+ ;Pitch up/down used?
+ ld a,(ix + PLY_AKM_Data_OffsetIsPitchUpDownUsed)
+ or a
+ jr z,PLY_AKM_ME_PitchUpDownFinished
+
+ ;Adds the LSB of integer part and decimal part, using one 16 bits operation.
+ ld l,(ix + PLY_AKM_Data_OffsetTrackPitchDecimal)
+ ld h,(ix + PLY_AKM_Data_OffsetTrackPitchInteger + 0)
+
+ ld e,(ix + PLY_AKM_Data_OffsetTrackPitchSpeed + 0)
+ ld d,(ix + PLY_AKM_Data_OffsetTrackPitchSpeed + 1)
+
+ ld a,(ix + PLY_AKM_Data_OffsetTrackPitchInteger + 1)
+
+ ;Negative pitch?
+ bit 7,d
+ jr nz,PLY_AKM_ME_PitchUpDown_NegativeSpeed
+
+PLY_AKM_ME_PitchUpDown_PositiveSpeed:
+ ;Positive speed. Adds it to the LSB of the integer part, and decimal part.
+ add hl,de
+
+ ;Carry? Transmits it to the MSB of the integer part.
+ adc 0
+ jr PLY_AKM_ME_PitchUpDown_Save
+PLY_AKM_ME_PitchUpDown_NegativeSpeed:
+ ;Negative speed. Resets the sign bit. The encoded pitch IS positive.
+ ;Subtracts it to the LSB of the integer part, and decimal part.
+ res 7,d
+
+ or a
+ sbc hl,de
+
+ ;Carry? Transmits it to the MSB of the integer part.
+ sbc 0
+
+PLY_AKM_ME_PitchUpDown_Save:
+ ld (ix + PLY_AKM_Data_OffsetTrackPitchInteger + 1),a
+
+ ld (ix + PLY_AKM_Data_OffsetTrackPitchDecimal),l
+ ld (ix + PLY_AKM_Data_OffsetTrackPitchInteger + 0),h
+
+PLY_AKM_ME_PitchUpDownFinished:
+ ENDIF ;PLY_AKM_USE_EffectPitchUpDown
+
+
+
+ ;Manages the Arpeggio Table effect, if any.
+ ;------------------------------------------
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+ ld a,(ix + PLY_AKM_Data_OffsetIsArpeggioTableUsed)
+ or a
+ jr z,PLY_AKM_ME_ArpeggioTableFinished
+
+ ;Plays the arpeggio current note. It is suppose to be correct (not a loop).
+ ;Plays it in any case, in order to manage some corner case with Force Arpeggio Speed.
+ ld e,(ix + PLY_AKM_Data_OffsetPtArpeggioTable + 0)
+ ld d,(ix + PLY_AKM_Data_OffsetPtArpeggioTable + 1)
+ ld l,(ix + PLY_AKM_Data_OffsetPtArpeggioOffset)
+ ld h,0
+ add hl,de
+ ld a,(hl) ;Gets the Arpeggio value (b1-b7).
+ sra a ;Carry is 0, because the ADD above surely didn't overflow.
+ ld (ix + PLY_AKM_Data_OffsetCurrentArpeggioValue),a
+
+ ;Moves forward, if the speed has been reached.
+ ;Has the speed been reached?
+ ld a,(ix + PLY_AKM_Data_OffsetArpeggioCurrentStep)
+ IFDEF PLY_CFG_UseEffect_ForceArpeggioSpeed ;CONFIG SPECIFIC
+ cp (ix + PLY_AKM_Data_OffsetArpeggioCurrentSpeed)
+ ELSE
+ cp (ix + PLY_AKM_Data_OffsetArpeggioOriginalSpeed)
+ ENDIF ;PLY_CFG_UseEffect_ForceArpeggioSpeed
+ jr c,PLY_AKM_ME_ArpeggioTable_SpeedNotReached
+ ;Resets the speed. Reads the next Arpeggio value.
+ ld (ix + PLY_AKM_Data_OffsetArpeggioCurrentStep),0
+
+ ;Advances in the Arpeggio.
+ inc (ix + PLY_AKM_Data_OffsetPtArpeggioOffset)
+ inc hl ;HL points on the next value. No need to add to the base offset like before, we have it.
+ ld a,(hl)
+ ;End of the Arpeggio?
+ rra ;Carry is 0.
+ jr nc,PLY_AKM_ME_ArpeggioTableFinished
+ ;End of the Arpeggio. The loop offset is now in A.
+ ld l,a
+ ld (ix + PLY_AKM_Data_OffsetPtArpeggioOffset),a
+ jr PLY_AKM_ME_ArpeggioTableFinished
+
+PLY_AKM_ME_ArpeggioTable_SpeedNotReached:
+ inc a
+ ld (ix + PLY_AKM_Data_OffsetArpeggioCurrentStep),a
+
+PLY_AKM_ME_ArpeggioTableFinished:
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+
+
+
+ ;Manages the Pitch Table effect, if any.
+ ;------------------------------------------
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+ ld a,(ix + PLY_AKM_Data_OffsetIsPitchTableUsed)
+ or a
+ ret z
+
+ ;Plays the Pitch Table current note. It is suppose to be correct (not a loop).
+ ;Plays it in any case, in order to manage some corner case with Force Pitch Speed.
+ ;Reads the Pitch Table. Adds the Pitch base address to an offset.
+ ld l,(ix + PLY_AKM_Data_OffsetPtPitchTable + 0)
+ ld h,(ix + PLY_AKM_Data_OffsetPtPitchTable + 1)
+ ld e,(ix + PLY_AKM_Data_OffsetPtPitchOffset)
+ ld d,0
+ add hl,de
+ ld a,(hl) ;Gets the Pitch value (b1-b7).
+ sra a
+ ;A = pitch note. It is converted to 16 bits.
+ ;D is already 0.
+ jp p,PLY_AKM_ME_PitchTableEndNotReached_Positive
+ dec d
+PLY_AKM_ME_PitchTableEndNotReached_Positive:
+ ld (ix + PLY_AKM_Data_OffsetCurrentPitchTableValue + 0),a
+ ld (ix + PLY_AKM_Data_OffsetCurrentPitchTableValue + 1),d
+
+ ;Moves forward, if the speed has been reached.
+ ;Has the speed been reached?
+ ld a,(ix + PLY_AKM_Data_OffsetPitchCurrentStep)
+ IFDEF PLY_CFG_UseEffect_ForcePitchTableSpeed ;CONFIG SPECIFIC
+ cp (ix + PLY_AKM_Data_OffsetPitchCurrentSpeed)
+ ELSE
+ cp (ix + PLY_AKM_Data_OffsetPitchOriginalSpeed)
+ ENDIF ;PLY_CFG_UseEffect_ForcePitchTableSpeed
+ jr c,PLY_AKM_ME_PitchTable_SpeedNotReached
+ ;Resets the speed, then reads the next Pitch value.
+ ld (ix + PLY_AKM_Data_OffsetPitchCurrentStep),0
+
+ ;Advances in the Pitch.
+ inc (ix + PLY_AKM_Data_OffsetPtPitchOffset)
+ inc hl ;HL points on the next value. No need to add to the base offset like before, we have it.
+ ld a,(hl)
+ ;End of the Pitch?
+ rra ;Carry is 0.
+ ret nc
+ ;End of the Pitch. The loop offset is now in A.
+ ld l,a
+ ld (ix + PLY_AKM_Data_OffsetPtPitchOffset),a
+ ret
+
+PLY_AKM_ME_PitchTable_SpeedNotReached:
+ inc a
+ ld (ix + PLY_AKM_Data_OffsetPitchCurrentStep),a
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+ ret
+ ENDIF ;PLY_CFG_UseEffects
+
+
+
+
+;---------------------------------------------------------------------
+;Sound stream.
+;---------------------------------------------------------------------
+
+;Plays the sound stream, filling the PSG registers table (but not playing it).
+;The Instrument pointer must be updated as it evolves inside the Instrument.
+;IN: IX = Data block of the Track.
+; IY = Points at the beginning of the register structure related to the channel.
+; C = R7. Only bit 2 (sound) must be *set* to cut the sound if needed, and bit 5 (noise) must be *reset* if there is noise.
+; DE' = Period table. Must not be modified.
+PLY_AKM_PlaySoundStream:
+ ;Gets the pointer on the Instrument, from its base address and the offset.
+ ld l,(ix + PLY_AKM_Data_OffsetPtInstrument + 0)
+ ld h,(ix + PLY_AKM_Data_OffsetPtInstrument + 1)
+
+ ;Reads the first byte of the cell of the Instrument. What type?
+PLY_AKM_PSS_ReadFirstByte:
+ ld a,(hl)
+ ld b,a
+ inc hl
+ rra
+ jr c,PLY_AKM_PSS_SoftOrSoftAndHard
+
+ ;NoSoftNoHard or SoftwareToHardware
+ rra
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+ jr c,PLY_AKM_PSS_SoftwareToHardware
+ ENDIF ;PLY_CFG_UseHardwareSounds
+
+ ;No software no hardware, or end of sound (loop)!
+ ;End of sound?
+ rra
+ jr nc,PLY_AKM_PSS_NSNH_NotEndOfSound
+ ;The sound loops/ends. Where?
+ ld a,(hl)
+ inc hl
+ ld h,(hl)
+ ld l,a
+ ;As a sound always has at least one cell, we should safely be able to read its bytes without storing the instrument pointer.
+ ;However, we do it anyway to remove the overhead of the Speed management: if looping, the same last line will be read,
+ ;if several channels do so, it will be costly. So...
+ ld (ix + PLY_AKM_Data_OffsetPtInstrument + 0),l
+ ld (ix + PLY_AKM_Data_OffsetPtInstrument + 1),h
+ jr PLY_AKM_PSS_ReadFirstByte
+
+PLY_AKM_PSS_NSNH_NotEndOfSound:
+ ;No software, no hardware.
+ ;-------------------------
+ ;Stops the sound.
+ set 2,c
+
+ ;Volume. A now contains the volume on b0-3.
+ IFDEF PLY_CFG_UseEffect_SetVolume ;CONFIG SPECIFIC
+ call PLY_AKM_PSS_Shared_AdjustVolume
+ ELSE
+ and %1111
+ ENDIF ;PLY_CFG_UseEffect_SetVolume
+ ld (iy + PLY_AKM_Registers_OffsetVolume),a
+
+ ;Read noise?
+ rl b
+ IFDEF PLY_CFG_NoSoftNoHard_Noise ;CONFIG SPECIFIC
+ call c,PLY_AKM_PSS_ReadNoise
+ ENDIF ;PLY_CFG_NoSoftNoHard_Noise
+ jr PLY_AKM_PSS_Shared_StoreInstrumentPointer
+
+ ;Software sound, or Software and Hardware?
+PLY_AKM_PSS_SoftOrSoftAndHard:
+ rra
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+ jr c,PLY_AKM_PSS_SoftAndHard
+ ENDIF ;PLY_CFG_UseHardwareSounds
+
+ ;Software sound.
+ ;-----------------
+ ;A is the volume. Already shifted twice, so it can be used directly.
+ IFDEF PLY_CFG_UseEffect_SetVolume ;CONFIG SPECIFIC
+ call PLY_AKM_PSS_Shared_AdjustVolume
+ ELSE
+ and %1111
+ ENDIF ;PLY_CFG_UseEffect_SetVolume
+ ld (iy + PLY_AKM_Registers_OffsetVolume),a
+
+ ;Arp and/or noise?
+ ld d,0 ;Default arpeggio.
+ rl b
+ jr nc,PLY_AKM_PSS_S_AfterArpAndOrNoise
+ ld a,(hl)
+ inc hl
+ ;Noise?
+ sra a
+ ;A is now the signed Arpeggio. It must be kept.
+ ld d,a
+ ;Now takes care of the noise, if there is a Carry.
+ IFDEF PLY_CFG_SoftOnly_Noise ;CONFIG SPECIFIC
+ call c,PLY_AKM_PSS_ReadNoise
+ ENDIF ;PLY_CFG_SoftOnly_Noise
+PLY_AKM_PSS_S_AfterArpAndOrNoise:
+
+ ld a,d ;Gets the instrument arpeggio, if any.
+ call PLY_AKM_CalculatePeriodForBaseNote
+
+ ;Read pitch?
+ rl b
+ IFDEF PLY_CFG_SoftOnly_SoftwarePitch ;CONFIG SPECIFIC
+ call c,PLY_AKM_ReadPitchAndAddToPeriod
+ ENDIF ;PLY_CFG_SoftOnly_SoftwarePitch
+
+ ;Stores the new period of this channel.
+ exx
+ ld (iy + PLY_AKM_Registers_OffsetSoftwarePeriodLSB),l
+ ld (iy + PLY_AKM_Registers_OffsetSoftwarePeriodMSB),h
+ exx
+
+ ;The code below is shared!
+ ;Stores the new instrument pointer, if Speed allows it.
+ ;--------------------------------------------------
+PLY_AKM_PSS_Shared_StoreInstrumentPointer:
+ ;Checks the Instrument speed, and only stores the Instrument new pointer if the speed is reached.
+ ld a,(ix + PLY_AKM_Data_OffsetInstrumentCurrentStep)
+ cp (ix + PLY_AKM_Data_OffsetInstrumentSpeed)
+ jr nc,PLY_AKM_PSS_S_SpeedReached
+ ;Increases the current step.
+ inc (ix + PLY_AKM_Data_OffsetInstrumentCurrentStep)
+ ret
+PLY_AKM_PSS_S_SpeedReached:
+ ;Stores the Instrument new pointer, resets the speed counter.
+ ld (ix + PLY_AKM_Data_OffsetPtInstrument + 0),l
+ ld (ix + PLY_AKM_Data_OffsetPtInstrument + 1),h
+ ld (ix + PLY_AKM_Data_OffsetInstrumentCurrentStep),0
+ ret
+
+
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+
+ ;Software and Hardware.
+ ;----------------------------
+PLY_AKM_PSS_SoftAndHard:
+ ;Reads the envelope bit, the possible pitch, and sets the software period accordingly.
+ call PLY_AKM_PSS_Shared_ReadEnvBitPitchArp_SoftPeriod_HardVol_HardEnv
+ ;Reads the hardware period.
+ ld a,(hl)
+ ld (PLY_AKM_Reg11),a
+ inc hl
+ ld a,(hl)
+ ld (PLY_AKM_Reg12),a
+ inc hl
+
+ jr PLY_AKM_PSS_Shared_StoreInstrumentPointer
+
+
+ ;Software to Hardware.
+ ;-------------------------
+PLY_AKM_PSS_SoftwareToHardware:
+ call PLY_AKM_PSS_Shared_ReadEnvBitPitchArp_SoftPeriod_HardVol_HardEnv
+
+ ;Now we can calculate the hardware period thanks to the ratio (contray to LW, it is NOT inverted, we can use it as-is).
+ ld a,b
+ rlca
+ rlca
+ rlca
+ rlca
+ and %111
+ exx
+ jr z,PLY_AKM_PSS_STH_RatioEnd
+PLY_AKM_PSS_STH_RatioLoop:
+ srl h
+ rr l
+ dec a
+ jr nz,PLY_AKM_PSS_STH_RatioLoop
+ ;If carry, rounds the period.
+ jr nc,PLY_AKM_PSS_STH_RatioEnd
+ inc hl
+PLY_AKM_PSS_STH_RatioEnd:
+ ld a,l
+ ld (PLY_AKM_Reg11),a
+ ld a,h
+ ld (PLY_AKM_Reg12),a
+ exx
+
+ jr PLY_AKM_PSS_Shared_StoreInstrumentPointer
+
+;A shared code for hardware sound.
+;Reads the envelope bit in bit 1, arpeggio in bit 7 pitch in bit 2 from A. If pitch present, adds it to BC'.
+;Converts the note to period, adds the instrument pitch, sets the software period of the channel.
+;Also sets the hardware volume, and sets the hardware curve.
+PLY_AKM_PSS_Shared_ReadEnvBitPitchArp_SoftPeriod_HardVol_HardEnv:
+ ;Envelope bit? R13 = 8 + 2 * (envelope bit?). Allows to have hardware envelope to 8 or 0xa.
+ ;Shifted by 2 to the right, bit 1 is now envelope bit, which is perfect for us.
+ and %10
+ add a,8
+ ld (PLY_AKM_SetReg13 + PLY_AKM_Offset1b),a
+
+ ;Volume to 16 to trigger the hardware envelope.
+ ld (iy + PLY_AKM_Registers_OffsetVolume),16
+
+ ;Arpeggio?
+ xor a ;Default arpeggio.
+ IFDEF PLY_AKM_ArpeggioInHardwareInstrument ;CONFIG SPECIFIC
+ bit 7,b ;Not shifted yet.
+ jr z,PLY_AKM_PSS_Shared_REnvBAP_AfterArpeggio
+ ;Reads the Arpeggio.
+ ld a,(hl)
+ inc hl
+PLY_AKM_PSS_Shared_REnvBAP_AfterArpeggio:
+ ENDIF ;PLY_AKM_ArpeggioInHardwareInstrument
+ ;Calculates the software period.
+ call PLY_AKM_CalculatePeriodForBaseNote
+
+ ;Pitch?
+ IFDEF PLY_AKM_PitchInHardwareInstrument ;CONFIG SPECIFIC
+ bit 2,b ;Not shifted yet.
+ call nz,PLY_AKM_ReadPitchAndAddToPeriod
+ ENDIF ;PLY_AKM_PitchInHardwareInstrument
+
+ ;Stores the new period of this channel.
+ exx
+ ld (iy + PLY_AKM_Registers_OffsetSoftwarePeriodLSB),l
+ ld (iy + PLY_AKM_Registers_OffsetSoftwarePeriodMSB),h
+ exx
+ ret
+
+ ENDIF ;PLY_CFG_UseHardwareSounds
+
+ IFDEF PLY_CFG_UseEffect_SetVolume ;CONFIG SPECIFIC
+;Decreases the given volume (encoded in possibly more then 4 bits). If <0, forced to 0.
+;IN: A = volume, not ANDed.
+;OUT: A = new volume.
+PLY_AKM_PSS_Shared_AdjustVolume:
+ and %1111
+ sub (ix + PLY_AKM_Data_OffsetTrackInvertedVolume)
+ ret nc
+ xor a
+ ret
+ ENDIF ;PLY_CFG_UseEffect_SetVolume
+
+;Reads and stores the noise pointed by HL, opens the noise channel.
+;IN: HL = instrument data where the noise is.
+;OUT: HL = HL++.
+;MOD: A.
+ IFDEF PLY_AKM_USE_Noise ;CONFIG SPECIFIC
+PLY_AKM_PSS_ReadNoise:
+ ld a,(hl)
+ inc hl
+ ld (PLY_AKM_NoiseRegister),a
+ res 5,c ;Opens the noise channel.
+ ret
+ ENDIF ;PLY_AKM_USE_Noise
+
+;Calculates the period according to the base note and put it in BC'. Used by both software and hardware codes.
+;IN: DE' = period table.
+; A = instrument arpeggio (0 if not used).
+;OUT: HL' = period.
+;MOD: A
+PLY_AKM_CalculatePeriodForBaseNote:
+ ;Gets the period from the current note.
+ exx
+ ld h,0
+ add a,(ix + PLY_AKM_Data_OffsetBaseNote) ;Adds the instrument Arp to the base note (including the transposition).
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+ add (ix + PLY_AKM_Data_OffsetCurrentArpeggioValue) ;Adds the Arpeggio Table effect.
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+
+ ;Finds the period from a single line of octave look-up table. This is slow...
+ ;IN: DE = PeriodTable.
+ ; A = note (>=0).
+ ;OUT: HL = period.
+ ; DE unmodified.
+ ; BC modified.
+
+ ;Finds the octave.
+dknr3 (void): ld bc,255 * 256 + 12 ;B = Octave (>=0). Will be increased just below.
+PLY_AKM_FindOctave_Loop:
+ inc b ;Next octave.
+ sub c
+ jr nc,PLY_AKM_FindOctave_Loop
+ add a,c ;Compensates the first iteration that may not have been useful.
+
+ ;A = note inside the octave. Gets the period for the note, for the lowest octave.
+ add a,a
+ ld l,a
+ ld h,0
+ add hl,de ;Points on the period on the lowest octave.
+ ld a,(hl)
+ inc hl
+ ld h,(hl) ;HL is the period on the lowest octave.
+ ld l,a
+ ;Divides the period as long as we haven't reached the octave.
+ ld a,b
+ or a
+ jr z,PLY_AKM_FindOctave_OctaveShiftLoop_Finished
+PLY_AKM_FindOctave_OctaveShiftLoop:
+ srl h
+ rr l
+ djnz PLY_AKM_FindOctave_OctaveShiftLoop ;Fortunately, does not modify the carry, used below.
+PLY_AKM_FindOctave_OctaveShiftLoop_Finished:
+ ;Rounds the period at the last iteration.
+ jr nc,PLY_AKM_FindOctave_Finished
+ inc hl
+PLY_AKM_FindOctave_Finished:
+
+ ;Adds the Pitch Table value, if used.
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+ ld a,(ix + PLY_AKM_Data_OffsetIsPitchTableUsed)
+ or a
+ jr z,PLY_AKM_CalculatePeriodForBaseNote_NoPitchTable
+ ld c,(ix + PLY_AKM_Data_OffsetCurrentPitchTableValue + 0)
+ ld b,(ix + PLY_AKM_Data_OffsetCurrentPitchTableValue + 1)
+ add hl,bc
+PLY_AKM_CalculatePeriodForBaseNote_NoPitchTable:
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+ ;Adds the Track Pitch.
+ IFDEF PLY_AKM_USE_EffectPitchUpDown ;CONFIG SPECIFIC
+ ld c,(ix + PLY_AKM_Data_OffsetTrackPitchInteger + 0)
+ ld b,(ix + PLY_AKM_Data_OffsetTrackPitchInteger + 1)
+ add hl,bc
+ ENDIF ;PLY_AKM_USE_EffectPitchUpDown
+ exx
+ ret
+
+ IFDEF PLY_AKM_PitchInInstrument ;CONFIG SPECIFIC
+;Reads the pitch in the Instruments (16 bits) and adds it to HL', which should contain the software period.
+;IN: HL = points on the pitch value.
+;OUT: HL = points after the pitch.
+;MOD: A, BC', HL' updated.
+PLY_AKM_ReadPitchAndAddToPeriod:
+ ;Reads 2 * 8 bits for the pitch. Slow...
+ ld a,(hl)
+ inc hl
+ exx
+ ld c,a ;Adds the read pitch to the note period.
+ exx
+ ld a,(hl)
+ inc hl
+ exx
+ ld b,a
+ add hl,bc
+ exx
+ ret
+ ENDIF ;PLY_AKM_PitchInInstrument
+
+
+
+
+
+
+
+;---------------------------------------------------------------------
+;Effect management.
+;---------------------------------------------------------------------
+
+ IFDEF PLY_CFG_UseEffects ;CONFIG SPECIFIC
+
+;IN: HL = points after the first byte.
+; A = data of the first byte on bits 0-3, the other bits are 0.
+; Carry = 0.
+; Z flag = 1 if the data is 0.
+; DE'= Instrument Table (not useful here). Do not modify!
+; IX = data block of the Track.
+; B = Do not modify!
+;OUT: HL = points after the data of the effect (maybe nothing to do).
+; Each effect must jump to PLY_AKM_RT_ReadEffect_Return.
+
+ IFDEF PLY_CFG_UseEffect_Reset ;CONFIG SPECIFIC.
+;Clears all the effects (volume, pitch table, arpeggio table).
+PLY_AKM_EffectResetWithVolume:
+ ;Inverted volume.
+ IFDEF PLY_CFG_UseEffect_SetVolume ;CONFIG SPECIFIC
+ ld (ix + PLY_AKM_Data_OffsetTrackInvertedVolume),a
+ ENDIF ;PLY_CFG_UseEffect_SetVolume
+ xor a
+ ;The inverted volume is managed above, so don't change it.
+ IFDEF PLY_AKM_USE_EffectPitchUpDown ;CONFIG SPECIFIC
+ ld (ix + PLY_AKM_Data_OffsetIsPitchUpDownUsed),a
+ ENDIF ;PLY_AKM_USE_EffectPitchUpDown
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+ ld (ix + PLY_AKM_Data_OffsetIsArpeggioTableUsed),a
+ ld (ix + PLY_AKM_Data_OffsetCurrentArpeggioValue),a ;Contrary to the Pitch, the value must be reset.
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+ ld (ix + PLY_AKM_Data_OffsetIsPitchTableUsed),a
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+ jp PLY_AKM_RT_ReadEffect_Return
+ ENDIF ;PLY_CFG_UseEffect_Reset
+
+
+;Changes the volume.
+ IFDEF PLY_CFG_UseEffect_SetVolume ;CONFIG SPECIFIC
+PLY_AKM_EffectVolume:
+ ld (ix + PLY_AKM_Data_OffsetTrackInvertedVolume),a
+ jp PLY_AKM_RT_ReadEffect_Return
+ ENDIF ;PLY_CFG_UseEffect_SetVolume
+
+ IFDEF PLY_CFG_UseEffect_ForceInstrumentSpeed ;CONFIG SPECIFIC
+;Forces the speed of the Instrument. The current step is NOT changed.
+PLY_AKM_EffectForceInstrumentSpeed:
+ call PLY_AKM_EffectReadIfEscape ;Makes sure the data is 0-14, else 15 means: read the next escape value.
+ ld (ix + PLY_AKM_Data_OffsetInstrumentSpeed),a
+
+ jp PLY_AKM_RT_ReadEffect_Return
+ ENDIF ;PLY_CFG_UseEffect_ForceInstrumentSpeed
+
+ IFDEF PLY_CFG_UseEffect_ForcePitchTableSpeed ;CONFIG SPECIFIC
+;Forces the speed of the Pitch. The current step is NOT changed.
+PLY_AKM_EffectForcePitchSpeed:
+ call PLY_AKM_EffectReadIfEscape ;Makes sure the data is 0-14, else 15 means: read the next escape value.
+ ld (ix + PLY_AKM_Data_OffsetPitchCurrentSpeed),a
+ ;ld (ix + PLY_AKM_Data_OffsetPitchCurrentStep),a ;No need to force next note of the Arpeggio. Faster, and more compliant with the C++ player.
+
+ jp PLY_AKM_RT_ReadEffect_Return
+ ENDIF ;PLY_CFG_UseEffect_ForcePitchTableSpeed
+
+ IFDEF PLY_CFG_UseEffect_ForceArpeggioSpeed ;CONFIG SPECIFIC
+;Forces the speed of the Arpeggio. The current step is NOT changed.
+PLY_AKM_EffectForceArpeggioSpeed:
+ call PLY_AKM_EffectReadIfEscape ;Makes sure the data is 0-14, else 15 means: read the next escape value.
+ ld (ix + PLY_AKM_Data_OffsetArpeggioCurrentSpeed),a
+ ;ld (ix + PLY_AKM_Data_OffsetArpeggioCurrentStep),a ;No need to force next note of the Arpeggio. Faster, and more compliant with the C++ player.
+
+ jp PLY_AKM_RT_ReadEffect_Return
+ ENDIF ;PLY_CFG_UseEffect_ForceArpeggioSpeed
+
+
+;Effect table. Each entry jumps to an effect management code.
+;Put after the code above so that the JR are within bound.
+PLY_AKM_EffectTable:
+ IFDEF PLY_CFG_UseEffect_Reset ;CONFIG SPECIFIC.
+ jr PLY_AKM_EffectResetWithVolume ;000
+ ELSE
+ jr $
+ ENDIF ;PLY_CFG_UseEffect_Reset
+
+ IFDEF PLY_CFG_UseEffect_SetVolume ;CONFIG SPECIFIC
+ jr PLY_AKM_EffectVolume ;001
+ ELSE
+ jr $
+ ENDIF ;PLY_CFG_UseEffect_SetVolume
+
+ IFDEF PLY_AKM_USE_EffectPitchUpDown ;CONFIG SPECIFIC
+ jr PLY_AKM_EffectPitchUpDown ;010
+ ELSE
+ jr $
+ ENDIF ;PLY_AKM_USE_EffectPitchUpDown
+
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+ jr PLY_AKM_EffectArpeggioTable ;011
+ ELSE
+ jr $
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+ jr PLY_AKM_EffectPitchTable ;100
+ ELSE
+ jr $
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+
+ IFDEF PLY_CFG_UseEffect_ForceInstrumentSpeed ;CONFIG SPECIFIC
+ jr PLY_AKM_EffectForceInstrumentSpeed ;101
+ ELSE
+ jr $
+ ENDIF ;PLY_CFG_UseEffect_ForceInstrumentSpeed
+
+ IFDEF PLY_CFG_UseEffect_ForceArpeggioSpeed ;CONFIG SPECIFIC
+ jr PLY_AKM_EffectForceArpeggioSpeed ;110
+ ELSE
+ jr $
+ ENDIF ;PLY_CFG_UseEffect_ForceArpeggioSpeed
+
+ IFDEF PLY_CFG_UseEffect_ForcePitchTableSpeed ;CONFIG SPECIFIC
+ jr PLY_AKM_EffectForcePitchSpeed ;111
+ ELSE
+ ;jr $ ;Last one. No need to encode it.
+ ENDIF ;PLY_CFG_UseEffect_ForcePitchTableSpeed
+
+
+ IFDEF PLY_AKM_USE_EffectPitchUpDown ;CONFIG SPECIFIC
+;Pitch up/down effect, activation or stop.
+PLY_AKM_EffectPitchUpDown:
+ rra ;Pitch present or pitch stop?
+ jr nc,PLY_AKM_EffectPitchUpDown_Deactivated
+ ;Activates the effect.
+ ld (ix + PLY_AKM_Data_OffsetIsPitchUpDownUsed),255
+ ld a,(hl)
+ inc hl
+ ld (ix + PLY_AKM_Data_OffsetTrackPitchSpeed + 0),a
+ ld a,(hl)
+ inc hl
+ ld (ix + PLY_AKM_Data_OffsetTrackPitchSpeed + 1),a
+ jp PLY_AKM_RT_ReadEffect_Return
+PLY_AKM_EffectPitchUpDown_Deactivated:
+ ;Pitch stop.
+ ld (ix + PLY_AKM_Data_OffsetIsPitchUpDownUsed),0
+ jp PLY_AKM_RT_ReadEffect_Return
+ ENDIF ;PLY_AKM_USE_EffectPitchUpDown
+
+
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+;Arpeggio table effect, activation or stop.
+PLY_AKM_EffectArpeggioTable:
+ call PLY_AKM_EffectReadIfEscape ;Makes sure the data is 0-14, else 15 means: read the next escape value.
+ ld (ix + PLY_AKM_Data_OffsetIsArpeggioTableUsed),a ;Sets to 0 if the Arpeggio is stopped, or any other value if it starts.
+ or a
+ jr z,PLY_AKM_EffectArpeggioTable_Stop
+
+ ;Gets the Arpeggio address.
+ add a,a
+ exx
+ ld l,a
+ ld h,0
+ ;BC is modified, will be restored below.
+ IFNDEF PLY_AKM_Rom
+dknr3 (void):
+PLY_AKM_PtArpeggios: ld bc,0 ;Arpeggio table does not encode entry 0, but the pointer points two bytes earlier to compensate.
+ ELSE
+ ld bc,(PLY_AKM_PtArpeggios)
+ ENDIF
+ add hl,bc
+ ld a,(hl)
+ inc hl
+ ld h,(hl)
+ ld l,a
+ ld a,(hl) ;Reads the speed.
+ inc hl
+ ld (ix + PLY_AKM_Data_OffsetArpeggioOriginalSpeed),a
+ IFDEF PLY_CFG_UseEffect_ForceArpeggioSpeed ;CONFIG SPECIFIC
+ ld (ix + PLY_AKM_Data_OffsetArpeggioCurrentSpeed),a
+ ENDIF ;PLY_CFG_UseEffect_ForceArpeggioSpeed
+ ld (ix + PLY_AKM_Data_OffsetPtArpeggioTable + 0),l
+ ld (ix + PLY_AKM_Data_OffsetPtArpeggioTable + 1),h
+
+ ld bc,(PLY_AKM_NoteIndexTable + PLY_AKM_Offset1b)
+ exx
+
+ ;Resets the offset of the Arpeggio to restart the Arpeggio, and forces a step to read immediately.
+ xor a
+ ld (ix + PLY_AKM_Data_OffsetPtArpeggioOffset),a
+ ld (ix + PLY_AKM_Data_OffsetArpeggioCurrentStep),a
+ jp PLY_AKM_RT_ReadEffect_Return
+PLY_AKM_EffectArpeggioTable_Stop:
+ ;Contrary to the Pitch, the Arpeggio must also be set to 0 when stopped.
+ ld (ix + PLY_AKM_Data_OffsetCurrentArpeggioValue),a
+ jp PLY_AKM_RT_ReadEffect_Return
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+;Pitch table effect, activation or stop.
+;This is almost exactly the same code as for the Arpeggio, but I can't find a way to share it...
+PLY_AKM_EffectPitchTable:
+ call PLY_AKM_EffectReadIfEscape ;Makes sure the data is 0-14, else 15 means: read the next escape value.
+ ld (ix + PLY_AKM_Data_OffsetIsPitchTableUsed),a ;Sets to 0 if the Pitch is stopped, or any other value if it starts.
+ or a
+ jp z,PLY_AKM_RT_ReadEffect_Return
+
+ ;Gets the Pitch address.
+ add a,a
+ exx
+ ld l,a
+ ld h,0
+ ;BC is modified, will be restored below.
+ IFNDEF PLY_AKM_Rom
+dknr3 (void):
+PLY_AKM_PtPitches: ld bc,0 ;Pitch table does not encode entry 0, but the pointer points two bytes earlier to compensate.
+ ELSE
+ ld bc,(PLY_AKM_PtPitches)
+ ENDIF
+ add hl,bc
+ ld a,(hl)
+ inc hl
+ ld h,(hl)
+ ld l,a
+ ld a,(hl) ;Reads the speed.
+ inc hl
+ ld (ix + PLY_AKM_Data_OffsetPitchOriginalSpeed),a
+ IFDEF PLY_CFG_UseEffect_ForcePitchTableSpeed ;CONFIG SPECIFIC
+ ld (ix + PLY_AKM_Data_OffsetPitchCurrentSpeed),a
+ ENDIF ;PLY_CFG_UseEffect_ForcePitchTableSpeed
+ ld (ix + PLY_AKM_Data_OffsetPtPitchTable + 0),l
+ ld (ix + PLY_AKM_Data_OffsetPtPitchTable + 1),h
+
+ ld bc,(PLY_AKM_NoteIndexTable + PLY_AKM_Offset1b)
+ exx
+
+ ;Resets the offset of the Pitch to restart the Pitch, and forces a step to read immediately.
+ xor a
+ ld (ix + PLY_AKM_Data_OffsetPtPitchOffset),a
+ ld (ix + PLY_AKM_Data_OffsetPitchCurrentStep),a
+ jp PLY_AKM_RT_ReadEffect_Return
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+
+
+
+
+
+
+;Reads the next escape byte if A is 15, else returns A (0-14).
+;IN: HL= data in the effect
+; A = 0-15. bit 7-4 must be 0.
+;OUT: HL= may be increased if an escape value is read.
+; A = the 8-bit value.
+PLY_AKM_EffectReadIfEscape:
+ cp 15
+ ret c
+ ;Reads the escape value.
+ ld a,(hl)
+ inc hl
+ ret
+
+ ENDIF ;PLY_CFG_UseEffects
+
+
+;---------------------------------------------------------------------
+;Data blocks for the three channels. Make sure NOTHING is added between, as the init clears everything!
+;---------------------------------------------------------------------
+
+ counter = 0
+ ;Macro to declare a DB if RAM player, or an increasing EQU for ROM player.
+ MACRO PLY_AKM_db label
+ IFNDEF PLY_AKM_Rom
+ dkbs (void)
+ {label} db 0
+ dkbe (void)
+ ELSE
+ {label} equ PLY_AKM_ROM_Buffer + counter
+ counter = counter + 1
+ ENDIF
+ ENDM
+
+ ;Macro to declare a DW if RAM player, or an increasing (of two bytes) EQU for ROM player.
+ MACRO PLY_AKM_dw label
+ IFNDEF PLY_AKM_Rom
+ dkws (void)
+ {label} dw 0
+ dkwe (void)
+ ELSE
+ {label} equ PLY_AKM_ROM_Buffer + counter
+ counter = counter + 2
+ ENDIF
+ ENDM
+
+
+ ;Specific generic data for ROM (non-related to channels).
+ ;Important: must be declared BEFORE the channel-specific data.
+ IFDEF PLY_AKM_Rom
+ PLY_AKM_dw PLY_AKM_PtInstruments
+ PLY_AKM_dw PLY_AKM_PtArpeggios
+ PLY_AKM_dw PLY_AKM_PtPitches
+ PLY_AKM_dw PLY_AKM_PtLinker
+ PLY_AKM_dw PLY_AKM_NoteIndexTable
+ PLY_AKM_dw PLY_AKM_TrackIndex
+ PLY_AKM_dw PLY_AKM_SaveSP
+
+ PLY_AKM_db PLY_AKM_DefaultStartNoteInTracks
+ PLY_AKM_db PLY_AKM_DefaultStartInstrumentInTracks
+ PLY_AKM_db PLY_AKM_DefaultStartWaitInTracks
+ PLY_AKM_db PLY_AKM_PrimaryInstrument
+ PLY_AKM_db PLY_AKM_SecondaryInstrument
+ PLY_AKM_db PLY_AKM_PrimaryWait
+ PLY_AKM_db PLY_AKM_SecondaryWait
+ PLY_AKM_db PLY_AKM_FlagNoteAndEffectInCell
+
+ PLY_AKM_db PLY_AKM_PatternRemainingHeight
+ PLY_AKM_db PLY_AKM_LinkerPreviousRemainingHeight
+ PLY_AKM_db PLY_AKM_Speed
+ PLY_AKM_db PLY_AKM_TickCounter
+ PLY_AKM_db PLY_AKM_SetReg13Old
+ PLY_AKM_db PLY_AKM_SetReg13
+ PLY_AKM_db PLY_AKM_RT_ReadEffectsFlag
+
+ ;RET table: db register, db value, dw code to jump to once the value is read.
+ ;MUST be consistent with the RAM buffer!
+PLY_AKM_Registers_RetTable: equ PLY_AKM_ROM_Buffer + counter
+ ;Reg 8.
+ PLY_AKM_db PLY_AKM_Track1_Registers
+ PLY_AKM_db PLY_AKM_Track1_Volume
+ PLY_AKM_dw PLY_AKM_Track1_VolumeRet
+ ;Reg 0.
+ PLY_AKM_db PLY_AKM_Track1_SoftwarePeriodLSBRegister
+ PLY_AKM_db PLY_AKM_Track1_SoftwarePeriodLSB
+ PLY_AKM_dw PLY_AKM_Track1_SoftwarePeriodLSBRet
+ ;Reg 1.
+ PLY_AKM_db PLY_AKM_Track1_SoftwarePeriodMSBRegister
+ PLY_AKM_db PLY_AKM_Track1_SoftwarePeriodMSB
+ PLY_AKM_dw PLY_AKM_Track1_SoftwarePeriodMSBRet
+
+ ;Reg 9.
+ PLY_AKM_db PLY_AKM_Track2_Registers
+ PLY_AKM_db PLY_AKM_Track2_Volume
+ PLY_AKM_dw PLY_AKM_Track2_VolumeRet
+ ;Reg 2.
+ PLY_AKM_db PLY_AKM_Track2_SoftwarePeriodLSBRegister
+ PLY_AKM_db PLY_AKM_Track2_SoftwarePeriodLSB
+ PLY_AKM_dw PLY_AKM_Track2_SoftwarePeriodLSBRet
+ ;Reg 3.
+ PLY_AKM_db PLY_AKM_Track2_SoftwarePeriodMSBRegister
+ PLY_AKM_db PLY_AKM_Track2_SoftwarePeriodMSB
+ PLY_AKM_dw PLY_AKM_Track2_SoftwarePeriodMSBRet
+
+ ;Reg 10.
+ PLY_AKM_db PLY_AKM_Track3_Registers
+ PLY_AKM_db PLY_AKM_Track3_Volume
+ PLY_AKM_dw PLY_AKM_Track3_VolumeRet
+ ;Reg 4.
+ PLY_AKM_db PLY_AKM_Track3_SoftwarePeriodLSBRegister
+ PLY_AKM_db PLY_AKM_Track3_SoftwarePeriodLSB
+ PLY_AKM_dw PLY_AKM_Track3_SoftwarePeriodLSBRet
+ ;Reg 5.
+ PLY_AKM_db PLY_AKM_Track3_SoftwarePeriodMSBRegister
+ PLY_AKM_db PLY_AKM_Track3_SoftwarePeriodMSB
+ PLY_AKM_dw PLY_AKM_Track3_SoftwarePeriodMSBRet
+
+ IFDEF PLY_AKM_USE_NoiseRegister ;CONFIG SPECIFIC
+ ;Reg 6.
+ PLY_AKM_db PLY_AKM_NoiseRegisterPlaceholder
+ PLY_AKM_db PLY_AKM_NoiseRegister ;Misnomer: this is the value.
+ PLY_AKM_dw PLY_AKM_NoiseRegisterRet
+ ENDIF
+
+ ;Reg 7.
+ PLY_AKM_db PLY_AKM_MixerRegisterPlaceholder
+ PLY_AKM_db PLY_AKM_MixerRegister ;Misnomer: this is the value.
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+ PLY_AKM_dw PLY_AKM_MixerRegisterRet
+ ;Reg 11.
+ PLY_AKM_db PLY_AKM_Reg11Register
+ PLY_AKM_db PLY_AKM_Reg11
+ PLY_AKM_dw PLY_AKM_Reg11Ret
+ ;Reg 12.
+ PLY_AKM_db PLY_AKM_Reg12Register
+ PLY_AKM_db PLY_AKM_Reg12
+ PLY_AKM_dw PLY_AKM_Reg12Ret
+ ;This one is a trick to send the register after R13 is managed.
+ PLY_AKM_dw PLY_AKM_Reg12Ret2
+ ENDIF
+
+ PLY_AKM_dw PLY_AKM_RegsFinalRet
+
+
+ ;The buffers for sound effects (if any), for each channel. They are treated apart, because they must be consecutive.
+ IFDEF PLY_AKM_MANAGE_SOUND_EFFECTS
+PLY_AKM_dw PLY_AKM_PtSoundEffectTable
+ REPEAT 3, channelNumber
+PLY_AKM_dw PLY_AKM_Channel{channelNumber}_SoundEffectData
+PLY_AKM_db PLY_AKM_Channel{channelNumber}_SoundEffectInvertedVolume
+PLY_AKM_db PLY_AKM_Channel{channelNumber}_SoundEffectCurrentStep
+PLY_AKM_db PLY_AKM_Channel{channelNumber}_SoundEffectSpeed
+ if channelNumber != 3
+ counter = counter + 3 ;Padding of 3, but only necessary for channel 1 and 2.
+ endif
+ REND
+ ENDIF ;PLY_AKM_MANAGE_SOUND_EFFECTS
+
+
+ ENDIF ;PLY_AKM_Rom
+
+;Data block for channel 1.
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_Track1_Data:
+ ELSE
+ counterStartInTrackData = counter ;Duplicates the counter value to determine later the size of the track buffer.
+ ENDIF
+ PLY_AKM_db PLY_AKM_Track1_WaitEmptyCell ;How many empty cells have to be waited. 0 = none.
+ IFDEF PLY_AKM_Rom
+ PLY_AKM_Track1_Data equ PLY_AKM_Track1_WaitEmptyCell
+ ENDIF
+ IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC
+ PLY_AKM_db PLY_AKM_Track1_Transposition
+ ENDIF ;PLY_CFG_UseTranspositions
+ PLY_AKM_dw PLY_AKM_Track1_PtStartTrack ;Points at the start of the Track to read. Does not change, unless the Track changes.
+ PLY_AKM_dw PLY_AKM_Track1_PtTrack ;Points on the next Cell of the Track to read. Evolves.
+ PLY_AKM_db PLY_AKM_Track1_BaseNote ;Base note, such as the note played. The transposition IS included.
+ PLY_AKM_db PLY_AKM_Track1_EscapeNote ;The escape note. The transposition is NOT included.
+ PLY_AKM_db PLY_AKM_Track1_EscapeInstrument ;The escape instrument.
+ PLY_AKM_db PLY_AKM_Track1_EscapeWait ;The escape wait.
+ PLY_AKM_dw PLY_AKM_Track1_PtInstrument ;Points on the Instrument, evolves.
+ PLY_AKM_db PLY_AKM_Track1_InstrumentCurrentStep ;The current step on the Instrument (>=0, till it reaches the Speed).
+ PLY_AKM_db PLY_AKM_Track1_InstrumentSpeed ;The Instrument speed (>=0).
+ IFDEF PLY_CFG_UseEffect_SetVolume ;CONFIG SPECIFIC
+ PLY_AKM_db PLY_AKM_Track1_TrackInvertedVolume
+ ENDIF ;PLY_CFG_UseEffect_SetVolume
+ IFDEF PLY_AKM_USE_EffectPitchUpDown ;CONFIG SPECIFIC
+ PLY_AKM_db PLY_AKM_Track1_IsPitchUpDownUsed ;>0 if a Pitch Up/Down is currently in use.
+ PLY_AKM_dw PLY_AKM_Track1_TrackPitchInteger ;The integer part of the Track pitch. Evolves as the pitch goes up/down.
+ PLY_AKM_db PLY_AKM_Track1_TrackPitchDecimal ;The decimal part of the Track pitch. Evolves as the pitch goes up/down.
+ PLY_AKM_dw PLY_AKM_Track1_TrackPitchSpeed ;The integer and decimal part of the Track pitch speed. Is added to the Track Pitch every frame.
+ ENDIF ;PLY_AKM_USE_EffectPitchUpDown
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+ PLY_AKM_db PLY_AKM_Track1_IsArpeggioTableUsed ;>0 if an Arpeggio Table is currently in use.
+ PLY_AKM_dw PLY_AKM_Track1_PtArpeggioTable ;Point on the base of the Arpeggio table, does not evolve.
+ PLY_AKM_db PLY_AKM_Track1_PtArpeggioOffset ;Increases over the Arpeggio.
+ PLY_AKM_db PLY_AKM_Track1_ArpeggioCurrentStep ;The arpeggio current step (>=0, increases).
+ IFDEF PLY_CFG_UseEffect_ForceArpeggioSpeed ;CONFIG SPECIFIC
+ PLY_AKM_db PLY_AKM_Track1_ArpeggioCurrentSpeed ;The arpeggio speed (>=0, may be changed by the Force Arpeggio Speed effect).
+ ENDIF ;PLY_CFG_UseEffect_ForceArpeggioSpeed
+ PLY_AKM_db PLY_AKM_Track1_ArpeggioOriginalSpeed ;The arpeggio original speed (>=0, NEVER changes for this arpeggio).
+ PLY_AKM_db PLY_AKM_Track1_CurrentArpeggioValue ;Value from the Arpeggio to add to the base note. Read even if the Arpeggio effect is deactivated.
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+ PLY_AKM_db PLY_AKM_Track1_IsPitchTableUsed ;>0 if a Pitch Table is currently in use.
+ PLY_AKM_dw PLY_AKM_Track1_PtPitchTable ;Points on the base of the Pitch table, does not evolve.
+ PLY_AKM_db PLY_AKM_Track1_PtPitchOffset ;Increases over the Pitch.
+ PLY_AKM_db PLY_AKM_Track1_PitchCurrentStep ;The Pitch current step (>=0, increases).
+ IFDEF PLY_CFG_UseEffect_ForcePitchTableSpeed ;CONFIG SPECIFIC
+ PLY_AKM_db PLY_AKM_Track1_PitchCurrentSpeed ;The Pitch speed (>=0, may be changed by the Force Pitch Speed effect).
+ ENDIF ;PLY_CFG_UseEffect_ForcePitchTableSpeed
+ PLY_AKM_db PLY_AKM_Track1_PitchOriginalSpeed ;The Pitch original speed (>=0, NEVER changes for this pitch).
+ PLY_AKM_dw PLY_AKM_Track1_CurrentPitchTableValue ;16 bit value from the Pitch to add to the base note. Not read if the Pitch effect is deactivated.
+
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_Track1_Data_End:
+PLY_AKM_Track1_Data_Size: equ PLY_AKM_Track1_Data_End - PLY_AKM_Track1_Data
+ ELSE
+PLY_AKM_Track1_Data_Size = counter - counterStartInTrackData
+PLY_AKM_Track1_Data_End = PLY_AKM_Track1_Data + PLY_AKM_Track1_Data_Size
+ ENDIF
+
+PLY_AKM_Data_OffsetWaitEmptyCell: equ PLY_AKM_Track1_WaitEmptyCell - PLY_AKM_Track1_Data
+ IFDEF PLY_CFG_UseTranspositions ;CONFIG SPECIFIC
+PLY_AKM_Data_OffsetTransposition: equ PLY_AKM_Track1_Transposition - PLY_AKM_Track1_Data
+ ENDIF ;PLY_CFG_UseTranspositions
+PLY_AKM_Data_OffsetPtStartTrack: equ PLY_AKM_Track1_PtStartTrack - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetPtTrack: equ PLY_AKM_Track1_PtTrack - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetBaseNote: equ PLY_AKM_Track1_BaseNote - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetEscapeNote: equ PLY_AKM_Track1_EscapeNote - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetEscapeInstrument: equ PLY_AKM_Track1_EscapeInstrument - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetEscapeWait: equ PLY_AKM_Track1_EscapeWait - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetSecondaryInstrument: equ PLY_AKM_Track1_EscapeWait - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetPtInstrument: equ PLY_AKM_Track1_PtInstrument - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetInstrumentCurrentStep: equ PLY_AKM_Track1_InstrumentCurrentStep - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetInstrumentSpeed: equ PLY_AKM_Track1_InstrumentSpeed - PLY_AKM_Track1_Data
+ IFDEF PLY_CFG_UseEffect_SetVolume ;CONFIG SPECIFIC
+PLY_AKM_Data_OffsetTrackInvertedVolume: equ PLY_AKM_Track1_TrackInvertedVolume - PLY_AKM_Track1_Data
+ ENDIF ;PLY_CFG_UseEffect_SetVolume
+ IFDEF PLY_AKM_USE_EffectPitchUpDown ;CONFIG SPECIFIC
+PLY_AKM_Data_OffsetIsPitchUpDownUsed: equ PLY_AKM_Track1_IsPitchUpDownUsed - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetTrackPitchInteger: equ PLY_AKM_Track1_TrackPitchInteger - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetTrackPitchDecimal: equ PLY_AKM_Track1_TrackPitchDecimal - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetTrackPitchSpeed: equ PLY_AKM_Track1_TrackPitchSpeed - PLY_AKM_Track1_Data
+ ENDIF ;PLY_AKM_USE_EffectPitchUpDown
+ IFDEF PLY_CFG_UseEffect_ArpeggioTable ;CONFIG SPECIFIC
+PLY_AKM_Data_OffsetIsArpeggioTableUsed: equ PLY_AKM_Track1_IsArpeggioTableUsed - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetPtArpeggioTable: equ PLY_AKM_Track1_PtArpeggioTable - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetPtArpeggioOffset: equ PLY_AKM_Track1_PtArpeggioOffset - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetArpeggioCurrentStep: equ PLY_AKM_Track1_ArpeggioCurrentStep - PLY_AKM_Track1_Data
+ IFDEF PLY_CFG_UseEffect_ForceArpeggioSpeed ;CONFIG SPECIFIC
+PLY_AKM_Data_OffsetArpeggioCurrentSpeed: equ PLY_AKM_Track1_ArpeggioCurrentSpeed - PLY_AKM_Track1_Data
+ ENDIF ;PLY_CFG_UseEffect_ForceArpeggioSpeed
+PLY_AKM_Data_OffsetArpeggioOriginalSpeed: equ PLY_AKM_Track1_ArpeggioOriginalSpeed - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetCurrentArpeggioValue: equ PLY_AKM_Track1_CurrentArpeggioValue - PLY_AKM_Track1_Data
+ ENDIF ;PLY_CFG_UseEffect_ArpeggioTable
+ IFDEF PLY_CFG_UseEffect_PitchTable ;CONFIG SPECIFIC
+PLY_AKM_Data_OffsetIsPitchTableUsed: equ PLY_AKM_Track1_IsPitchTableUsed - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetPtPitchTable: equ PLY_AKM_Track1_PtPitchTable - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetPtPitchOffset: equ PLY_AKM_Track1_PtPitchOffset - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetPitchCurrentStep: equ PLY_AKM_Track1_PitchCurrentStep - PLY_AKM_Track1_Data
+ IFDEF PLY_CFG_UseEffect_ForcePitchTableSpeed ;CONFIG SPECIFIC
+PLY_AKM_Data_OffsetPitchCurrentSpeed: equ PLY_AKM_Track1_PitchCurrentSpeed - PLY_AKM_Track1_Data
+ ENDIF ;PLY_CFG_UseEffect_ForcePitchTableSpeed
+PLY_AKM_Data_OffsetPitchOriginalSpeed: equ PLY_AKM_Track1_PitchOriginalSpeed - PLY_AKM_Track1_Data
+PLY_AKM_Data_OffsetCurrentPitchTableValue: equ PLY_AKM_Track1_CurrentPitchTableValue - PLY_AKM_Track1_Data
+ ENDIF ;PLY_CFG_UseEffect_PitchTable
+
+;Data block for channel 2.
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_Track2_Data:
+dkbs (void):
+ ds PLY_AKM_Track1_Data_Size, 0
+dkbe (void):
+PLY_AKM_Track2_Data_End:
+ ELSE
+PLY_AKM_Track2_Data: equ PLY_AKM_Track1_Data + PLY_AKM_Track1_Data_Size
+PLY_AKM_Track2_Data_End: equ PLY_AKM_Track2_Data + PLY_AKM_Track1_Data_Size
+ ENDIF
+PLY_AKM_Track2_WaitEmptyCell: equ PLY_AKM_Track2_Data + PLY_AKM_Data_OffsetWaitEmptyCell
+PLY_AKM_Track2_PtTrack: equ PLY_AKM_Track2_Data + PLY_AKM_Data_OffsetPtTrack
+PLY_AKM_Track2_PtInstrument: equ PLY_AKM_Track2_Data + PLY_AKM_Data_OffsetPtInstrument
+PLY_AKM_Track2_EscapeNote: equ PLY_AKM_Track2_Data + PLY_AKM_Data_OffsetEscapeNote
+PLY_AKM_Track2_EscapeInstrument: equ PLY_AKM_Track2_Data + PLY_AKM_Data_OffsetEscapeInstrument
+PLY_AKM_Track2_EscapeWait: equ PLY_AKM_Track2_Data + PLY_AKM_Data_OffsetEscapeWait
+
+;Data block for channel 3.
+ IFNDEF PLY_AKM_Rom
+PLY_AKM_Track3_Data:
+dkbs (void):
+ ds PLY_AKM_Track1_Data_Size, 0
+dkbe (void):
+PLY_AKM_Track3_Data_End:
+ ELSE
+PLY_AKM_Track3_Data: equ PLY_AKM_Track2_Data + PLY_AKM_Track1_Data_Size
+PLY_AKM_Track3_Data_End: equ PLY_AKM_Track3_Data + PLY_AKM_Track1_Data_Size
+
+PLY_AKM_ROM_Buffer_End: equ PLY_AKM_Track3_Data_End
+PLY_AKM_ROM_BufferSize: equ PLY_AKM_ROM_Buffer_End - PLY_AKM_ROM_Buffer
+
+ expectedRomBufferSize = 199 ;Just to detect if the buffer grows.
+ IFNDEF PLY_AKM_MANAGE_SOUND_EFFECTS
+ assert PLY_AKM_ROM_BufferSize <= expectedRomBufferSize ;Decreases when using the Player Configuration.
+ ELSE
+ assert PLY_AKM_ROM_BufferSize <= (expectedRomBufferSize + 23) ;With sound effects, it takes a bit more memory.
+ ENDIF
+ ENDIF
+
+
+
+
+PLY_AKM_Track3_WaitEmptyCell: equ PLY_AKM_Track3_Data + PLY_AKM_Data_OffsetWaitEmptyCell
+PLY_AKM_Track3_PtTrack: equ PLY_AKM_Track3_Data + PLY_AKM_Data_OffsetPtTrack
+PLY_AKM_Track3_PtInstrument: equ PLY_AKM_Track3_Data + PLY_AKM_Data_OffsetPtInstrument
+PLY_AKM_Track3_EscapeNote: equ PLY_AKM_Track3_Data + PLY_AKM_Data_OffsetEscapeNote
+PLY_AKM_Track3_EscapeInstrument: equ PLY_AKM_Track3_Data + PLY_AKM_Data_OffsetEscapeInstrument
+PLY_AKM_Track3_EscapeWait: equ PLY_AKM_Track3_Data + PLY_AKM_Data_OffsetEscapeWait
+
+ ;Makes sure the structure all have the same size!
+ ASSERT (PLY_AKM_Track1_Data_End - PLY_AKM_Track1_Data) == (PLY_AKM_Track2_Data_End - PLY_AKM_Track2_Data)
+ ASSERT (PLY_AKM_Track1_Data_End - PLY_AKM_Track1_Data) == (PLY_AKM_Track3_Data_End - PLY_AKM_Track3_Data)
+ ;No holes between the blocks, the init makes a LDIR to clear everything!
+ ASSERT PLY_AKM_Track1_Data_End == PLY_AKM_Track2_Data
+ ASSERT PLY_AKM_Track2_Data_End == PLY_AKM_Track3_Data
+
+
+
+;---------------------------------------------------------------------
+;Register block for all the channels. They are "polluted" with pointers to code because all this
+;is actually a RET table!
+;---------------------------------------------------------------------
+;DB register, DB value then DW code to jump to once the value is read.
+ IFNDEF PLY_AKM_Rom ;For ROM, a table is generated.
+PLY_AKM_Registers_RetTable:
+PLY_AKM_Track1_Registers:
+dkbs (void): db 8
+PLY_AKM_Track1_Volume: db 0
+dkbe (void):
+dkps (void): dw PLY_AKM_SendPsgRegister
+dkpe (void):
+
+dkbs (void): db 0
+PLY_AKM_Track1_SoftwarePeriodLSB: db 0
+dkbe (void):
+dkps (void): dw PLY_AKM_SendPsgRegister
+dkpe (void):
+
+dkbs (void): db 1
+PLY_AKM_Track1_SoftwarePeriodMSB: db 0
+dkbe (void):
+dkps (void): dw PLY_AKM_SendPsgRegister
+dkpe (void):
+
+PLY_AKM_Track2_Registers:
+dkbs (void): db 9
+PLY_AKM_Track2_Volume: db 0
+dkbe (void):
+dkps (void): dw PLY_AKM_SendPsgRegister
+dkpe (void):
+
+dkbs (void): db 2
+PLY_AKM_Track2_SoftwarePeriodLSB: db 0
+dkbe (void):
+dkps (void): dw PLY_AKM_SendPsgRegister
+dkpe (void):
+
+dkbs (void): db 3
+PLY_AKM_Track2_SoftwarePeriodMSB: db 0
+dkbe (void):
+dkps (void): dw PLY_AKM_SendPsgRegister
+dkpe (void):
+
+
+PLY_AKM_Track3_Registers:
+dkbs (void): db 10
+PLY_AKM_Track3_Volume: db 0
+dkbe (void):
+dkps (void): dw PLY_AKM_SendPsgRegister
+dkpe (void):
+
+dkbs (void): db 4
+PLY_AKM_Track3_SoftwarePeriodLSB: db 0
+dkbe (void):
+dkps (void): dw PLY_AKM_SendPsgRegister
+dkpe (void):
+
+dkbs (void): db 5
+PLY_AKM_Track3_SoftwarePeriodMSB: db 0
+dkbe (void):
+dkps (void): dw PLY_AKM_SendPsgRegister
+dkpe (void):
+
+;Generic registers.
+ IFDEF PLY_AKM_USE_NoiseRegister ;CONFIG SPECIFIC
+dkbs (void): db 6
+PLY_AKM_NoiseRegister: db 0
+dkbe (void):
+dkps (void): dw PLY_AKM_SendPsgRegister
+dkpe (void):
+ ENDIF ;PLY_AKM_USE_NoiseRegister
+
+dkbs (void): db 7
+PLY_AKM_MixerRegister: db 0
+dkbe (void):
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+dkps (void): dw PLY_AKM_SendPsgRegister
+dkpe (void):
+
+dkbs (void): db 11
+PLY_AKM_Reg11: db 0
+dkbe (void):
+dkps (void): dw PLY_AKM_SendPsgRegister
+dkpe (void):
+
+dkbs (void): db 12
+PLY_AKM_Reg12: db 0
+dkbe (void):
+dkps (void): dw PLY_AKM_SendPsgRegisterR13
+ ;This one is a trick to send the register after R13 is managed.
+ dw PLY_AKM_SendPsgRegisterAfterPop
+dkpe (void):
+ ENDIF ;PLY_CFG_UseHardwareSounds
+dkps (void): dw PLY_AKM_SendPsgRegisterEnd
+dkpe (void):
+
+ ENDIF ;PLY_AKM_Rom
+
+
+PLY_AKM_Registers_OffsetVolume: equ PLY_AKM_Track1_Volume - PLY_AKM_Track1_Registers
+PLY_AKM_Registers_OffsetSoftwarePeriodLSB: equ PLY_AKM_Track1_SoftwarePeriodLSB - PLY_AKM_Track1_Registers
+PLY_AKM_Registers_OffsetSoftwarePeriodMSB: equ PLY_AKM_Track1_SoftwarePeriodMSB - PLY_AKM_Track1_Registers
+
+;The period table for the first octave only.
+PLY_AKM_PeriodTable:
+dkws (void):
+ IFDEF PLY_AKM_HARDWARE_CPC
+ ;PSG running to 1000000 Hz.
+ dw 3822,3608,3405,3214,3034,2863,2703,2551,2408,2273,2145,2025 ; Octave 0.
+ ;dw 1911,1804,1703,1607,1517,1432,1351,1276,1204,1136,1073,1012 ;12
+ ;dw 956, 902, 851, 804, 758, 716, 676, 638, 602, 568, 536, 506 ;24
+ ;dw 478, 451, 426, 402, 379, 358, 338, 319, 301, 284, 268, 253 ;36
+ ;dw 239, 225, 213, 201, 190, 179, 169, 159, 150, 142, 134, 127 ;48
+ ;dw 119, 113, 106, 100, 95, 89, 84, 80, 75, 71, 67, 63 ;60
+ ;dw 60, 56, 53, 50, 47, 45, 42, 40, 38, 36, 34, 32 ;72
+ ;dw 30, 28, 27, 25, 24, 22, 21, 20, 19, 18, 17, 16 ;84
+ ;dw 15, 14, 13, 13, 12, 11, 11, 10, 9, 9, 8, 8 ;96
+ ;dw 7, 7, 7, 6, 6, 6, 5, 5, 5, 4, 4, 4 ;108
+ ;dw 4, 4, 3, 3, 3, 3, 3, 2 ;,2, 2, 2, 2 ;120 -> 127
+ ENDIF
+
+ IFDEF PLY_AKM_HARDWARE_SPECTRUM_OR_MSX
+ ;PSG running to 1773400 Hz.
+ dw 6778, 6398, 6039, 5700, 5380, 5078, 4793, 4524, 4270, 4030, 3804, 3591 ; Octave 0.
+ ENDIF
+
+ IFDEF PLY_AKM_HARDWARE_PENTAGON
+ ;PSG running to 1750000 Hz.
+ dw 6689, 6314, 5959, 5625, 5309, 5011, 4730, 4464, 4214, 3977, 3754, 3543 ; Octave 0.
+ ENDIF
+dkwe (void):
+PLY_AKM_End:
+
+
+
diff --git a/src/mplayer/akm/PlayerAkm_SoundEffects.asm b/src/mplayer/akm/PlayerAkm_SoundEffects.asm
new file mode 100644
index 0000000..1e4c3b8
--- /dev/null
+++ b/src/mplayer/akm/PlayerAkm_SoundEffects.asm
@@ -0,0 +1,477 @@
+; Player of sound effects, for the AKM player.
+; Note that this is the exact same code as for the Lightweight player, as the way to output to the PSG is the same.
+
+ ;Is there a loaded Player Configuration source? If no, use a default configuration.
+ IFNDEF PLY_CFG_SFX_ConfigurationIsPresent
+ PLY_CFG_UseHardwareSounds = 1
+ PLY_CFG_SFX_LoopTo = 1
+ PLY_CFG_SFX_NoSoftNoHard = 1
+ PLY_CFG_SFX_NoSoftNoHard_Noise = 1
+ PLY_CFG_SFX_SoftOnly = 1
+ PLY_CFG_SFX_SoftOnly_Noise = 1
+ PLY_CFG_SFX_HardOnly = 1
+ PLY_CFG_SFX_HardOnly_Noise = 1
+ PLY_CFG_SFX_HardOnly_Retrig = 1
+ PLY_CFG_SFX_SoftAndHard = 1
+ PLY_CFG_SFX_SoftAndHard_Noise = 1
+ PLY_CFG_SFX_SoftAndHard_Retrig = 1
+ ENDIF
+
+; Agglomerates some Player Configuration flags.
+; --------------------------------------------
+; Mixes the Hardware flags into one.
+ IFDEF PLY_CFG_SFX_HardOnly
+ PLY_AKM_SE_HardwareSounds = 1
+ ENDIF
+ IFDEF PLY_CFG_SFX_SoftAndHard
+ PLY_AKM_SE_HardwareSounds = 1
+ ENDIF
+; Mixes the Hardware Noise flags into one.
+ IFDEF PLY_CFG_SFX_HardOnly_Noise
+ PLY_AKM_SE_HardwareNoise = 1
+ ENDIF
+ IFDEF PLY_CFG_SFX_SoftAndHard_Noise
+ PLY_AKM_SE_HardwareNoise = 1
+ ENDIF
+; Mixes the Noise flags into one.
+ IFDEF PLY_AKM_SE_HardwareNoise
+ PLY_AKM_SE_Noise = 1
+ ENDIF
+ IFDEF PLY_CFG_SFX_NoSoftNoHard_Noise
+ PLY_AKM_SE_Noise = 1
+ ENDIF
+ IFDEF PLY_CFG_SFX_SoftOnly
+ PLY_AKM_SE_Noise = 1
+ ENDIF
+; Noise in Sound Effects? Then noise register code must be compiled.
+ IFDEF PLY_AKM_SE_Noise
+ PLY_AKM_USE_NoiseRegister = 1
+ ENDIF
+; Mixes the Software Volume flags into one.
+ IFDEF PLY_CFG_SFX_NoSoftNoHard
+ PLY_AKM_SE_VolumeSoft = 1
+ PLY_AKM_SE_VolumeSoftOrHard = 1
+ ENDIF
+ IFDEF PLY_CFG_SFX_SoftOnly
+ PLY_AKM_SE_VolumeSoft = 1
+ PLY_AKM_SE_VolumeSoftOrHard = 1
+ ENDIF
+; Mixes the volume (soft/hard) into one.
+ IFDEF PLY_CFG_UseHardwareSounds
+ PLY_AKM_SE_VolumeSoftOrHard = 1
+ ENDIF
+; Mixes the retrig flags into one.
+ IFDEF PLY_CFG_SFX_HardOnly_Retrig
+ PLY_AKM_SE_Retrig = 1
+ ENDIF
+ IFDEF PLY_CFG_SFX_SoftAndHard_Retrig
+ PLY_AKM_SE_Retrig = 1
+ ENDIF
+
+
+;Initializes the sound effects. It MUST be called at any times before a first sound effect is triggered.
+;It doesn't matter whether the song is playing or not, or if it has been initialized or not.
+;IN: HL = Address to the sound effects data.
+PLY_AKM_InitSoundEffectsDisarkGenerateExternalLabel:
+PLY_AKM_InitSoundEffects:
+ ld (PLY_AKM_PtSoundEffectTable + PLY_AKM_Offset1b),hl
+ ret
+
+
+;Pl²ys a sound effect. If a previous one was already playing on the same channel, it is replaced.
+;This does not actually plays the sound effect, but programs its playing.
+;The music player, when called, will call the PLY_AKM_PlaySoundEffectsStream method below.
+;IN: A = Sound effect number (>0!).
+; C = The channel where to play the sound effect (0, 1, 2).
+; B = Inverted volume (0 = full volume, 16 = no sound). Hardware sounds are also lowered.
+PLY_AKM_PlaySoundEffectDisarkGenerateExternalLabel:
+PLY_AKM_PlaySoundEffect:
+ ;Gets the address to the sound effect.
+ dec a ;The 0th is not encoded.
+ IFNDEF PLY_AKM_Rom
+dknr3 (void):
+PLY_AKM_PtSoundEffectTable: ld hl,0
+ ELSE
+ ld hl,(PLY_AKM_PtSoundEffectTable)
+ ENDIF
+ ld e,a
+ ld d,0
+ add hl,de
+ add hl,de
+ ld e,(hl)
+ inc hl
+ ld d,(hl)
+ ;Reads the header of the sound effect to get the speed.
+ ld a,(de)
+ inc de
+ ex af,af'
+
+ ld a,b
+
+ ;Finds the pointer to the sound effect of the desired channel.
+ ld hl,PLY_AKM_Channel1_SoundEffectData
+ ld b,0
+ sla c
+ sla c
+ sla c
+ add hl,bc
+ ld (hl),e
+ inc hl
+ ld (hl),d
+ inc hl
+
+ ;Now stores the inverted volume.
+ ld (hl),a
+ inc hl
+
+ ;Resets the current speed, stores the instrument speed.
+ ld (hl),0
+ inc hl
+ ex af,af'
+ ld (hl),a
+
+ ret
+
+;Stops a sound effect. Nothing happens if there was no sound effect.
+;IN: A = The channel where to stop the sound effect (0, 1, 2).
+PLY_AKM_StopSoundEffectFromChannelDisarkGenerateExternalLabel:
+PLY_AKM_StopSoundEffectFromChannel:
+ ;Puts 0 to the pointer of the sound effect.
+ add a,a
+ add a,a
+ add a,a
+ ld e,a
+ ld d,0
+ ld hl,PLY_AKM_Channel1_SoundEffectData
+ add hl,de
+ ld (hl),d ;0 means "no sound".
+ inc hl
+ ld (hl),d
+ ret
+
+;Plays the sound effects, if any has been triggered by the user.
+;This does not actually send registers to the PSG, it only overwrite the required values of the registers of the player.
+;The sound effects initialization method must have been called before!
+;As R7 is required, this must be called after the music has been played, but BEFORE the registers are sent to the PSG.
+;IN: A = R7.
+PLY_AKM_PlaySoundEffectsStream:
+ ;Shifts the R7 to the left twice, so that bit 2 and 5 only can be set for each track, below.
+ rla
+ rla
+
+ ;Plays the sound effects on every track.
+ ld ix,PLY_AKM_Channel1_SoundEffectData
+ ld iy,PLY_AKM_Track1_Registers
+ ld c,a
+ call PLY_AKM_PSES_Play
+ ld ix,PLY_AKM_Channel2_SoundEffectData
+ ld iy,PLY_AKM_Track2_Registers
+ srl c ;Not RR, to make sure bit 6 is 0 (else, no more keyboard on CPC!).
+ ;Also, on MSX, bit 6 must be 0.
+ call PLY_AKM_PSES_Play
+ ld ix,PLY_AKM_Channel3_SoundEffectData
+ ld iy,PLY_AKM_Track3_Registers
+ IFDEF PLY_AKM_HARDWARE_MSX
+ scf ;On MSX, bit 7 must be 1.
+ rr c
+ ELSE
+ rr c ;On other platforms, we don't care about b7.
+ ENDIF
+ call PLY_AKM_PSES_Play
+
+ ld a,c
+ ld (PLY_AKM_MixerRegister),a
+ ret
+
+
+;Plays the sound stream from the given pointer to the sound effect. If 0, no sound is played.
+;The given R7 is given shift twice to the left, so that this code MUST set/reset the bit 2 (sound), and set/reset bit 5 (noise).
+;This code MUST overwrite these bits because sound effects have priority over the music.
+;IN: IX = Points on the sound effect pointer. If the sound effect pointer is 0, nothing must be played.
+; IY = Points at the beginning of the register structure related to the channel.
+; C = R7, shifted twice to the left.
+;OUT: The pointed pointer by IX may be modified as the sound advances.
+; C = R7, MUST be modified if there is a sound effect.
+PLY_AKM_PSES_Play:
+ ;Reads the pointer pointed by IX.
+ ld l,(ix + 0)
+ ld h,(ix + 1)
+ ld a,l
+ or h
+ ret z ;No sound to be played? Returns immediately.
+
+ ;Reads the first byte. What type of sound is it?
+PLY_AKM_PSES_ReadFirstByte:
+ ld a,(hl)
+ inc hl
+ ld b,a
+ rra
+ jr c,PLY_AKM_PSES_SoftwareOrSoftwareAndHardware
+ rra
+ IFDEF PLY_CFG_SFX_HardOnly ;CONFIG SPECIFIC
+ jr c,PLY_AKM_PSES_HardwareOnly
+ ENDIF ;PLY_CFG_SFX_HardOnly
+
+ ;No software, no hardware, or end/loop.
+ ;-------------------------------------------
+ ;End or loop?
+ rra
+ IFDEF PLY_CFG_SFX_NoSoftNoHard ;CONFIG SPECIFIC. If not present, the jump is not needed, the method is just below.
+ jr c,PLY_AKM_PSES_S_EndOrLoop
+
+ ;No software, no hardware.
+ ;-------------------------------------------
+ ;Gets the volume.
+ call PLY_AKM_PSES_ManageVolumeFromA_Filter4Bits
+
+ ;Noise?
+ IFDEF PLY_CFG_SFX_NoSoftNoHard_Noise ;CONFIG SPECIFIC
+ rl b
+ call PLY_AKM_PSES_ReadNoiseIfNeededAndOpenOrCloseNoiseChannel
+ ELSE
+ set 5,c ;No noise in compilation, so stops the noise.
+ ENDIF ;PLY_CFG_SFX_NoSoftNoHard_Noise
+
+ ;Cuts the sound.
+ set 2,c
+
+ jr PLY_AKM_PSES_SavePointerAndExit
+ ENDIF ;PLY_CFG_SFX_NoSoftNoHard
+
+ ;**Warning!** Do not put any instruction between EndOrLoop and NoSoftNoHard.
+
+PLY_AKM_PSES_S_EndOrLoop:
+ IFDEF PLY_CFG_SFX_LoopTo ;CONFIG SPECIFIC. If no "loop to", the sounds always end, no need to test.
+ ;Is it an end?
+ rra
+ jr c,PLY_AKM_PSES_S_Loop
+ ENDIF ;PLY_CFG_SFX_LoopTo
+ ;End of the sound. Marks the sound pointer with 0, meaning "no sound".
+ xor a
+ ld (ix + 0),a
+ ld (ix + 1),a
+ ret
+ IFDEF PLY_CFG_SFX_LoopTo ;CONFIG SPECIFIC.
+PLY_AKM_PSES_S_Loop:
+ ;Loops. Reads the pointer and directly uses it.
+ ld a,(hl)
+ inc hl
+ ld h,(hl)
+ ld l,a
+ jr PLY_AKM_PSES_ReadFirstByte
+ ENDIF ;PLY_CFG_SFX_LoopTo
+
+
+;Saves HL into IX, and exits. This must be called at the end of each Cell.
+;If the speed has not been reached, it is not saved.
+PLY_AKM_PSES_SavePointerAndExit:
+ ;Speed reached?
+ ld a,(ix + PLY_AKM_SoundEffectData_OffsetCurrentStep)
+ cp (ix + PLY_AKM_SoundEffectData_OffsetSpeed)
+ jr c,PLY_AKM_PSES_NotReached
+ ;The speed has been reached, so resets it and saves the pointer to the next cell to read.
+ ld (ix + PLY_AKM_SoundEffectData_OffsetCurrentStep),0
+ ld (ix + 0),l
+ ld (ix + 1),h
+ ret
+PLY_AKM_PSES_NotReached:
+ ;Speed not reached. Increases it, that's all. The same cell will be read next time.
+ inc (ix + PLY_AKM_SoundEffectData_OffsetCurrentStep)
+ ret
+
+ IFDEF PLY_CFG_SFX_HardOnly ;CONFIG SPECIFIC
+ ;Hardware only.
+ ;-------------------------------------------
+PLY_AKM_PSES_HardwareOnly:
+ ;Calls the shared code that manages everything.
+ call PLY_AKM_PSES_Shared_ReadRetrigHardwareEnvPeriodNoise
+ ;Cuts the sound.
+ set 2,c
+
+ jr PLY_AKM_PSES_SavePointerAndExit
+ ENDIF ;PLY_CFG_SFX_HardOnly
+
+
+
+PLY_AKM_PSES_SoftwareOrSoftwareAndHardware:
+ ;Software only?
+ rra
+ IFDEF PLY_CFG_SFX_SoftAndHard ;CONFIG SPECIFIC
+ jr c,PLY_AKM_PSES_SoftwareAndHardware
+ ENDIF ;PLY_CFG_SFX_SoftAndHard
+
+ ;Software.
+ ;-------------------------------------------
+ IFDEF PLY_CFG_SFX_SoftOnly ;CONFIG SPECIFIC
+ ;Volume.
+ call PLY_AKM_PSES_ManageVolumeFromA_Filter4Bits
+
+ ;Noise?
+ IFDEF PLY_CFG_SFX_SoftOnly_Noise ;CONFIG SPECIFIC
+ rl b
+ call PLY_AKM_PSES_ReadNoiseIfNeededAndOpenOrCloseNoiseChannel
+ ELSE
+ set 5,c ;No noise in compilation, so stops the noise.
+ ENDIF ;PLY_CFG_SFX_SoftOnly_Noise
+
+ ;Opens the "sound" channel.
+ res 2,c
+
+ ;Reads the software period.
+ call PLY_AKM_PSES_ReadSoftwarePeriod
+
+ jr PLY_AKM_PSES_SavePointerAndExit
+ ENDIF ;PLY_CFG_SFX_SoftOnly
+
+
+ ;Software and Hardware.
+ ;-------------------------------------------
+ IFDEF PLY_AKM_SE_HardwareSounds ;CONFIG SPECIFIC
+PLY_AKM_PSES_SoftwareAndHardware:
+ ;Calls the shared code that manages everything.
+ call PLY_AKM_PSES_Shared_ReadRetrigHardwareEnvPeriodNoise
+
+ ;Reads the software period.
+ call PLY_AKM_PSES_ReadSoftwarePeriod
+
+ ;Opens the sound.
+ res 2,c
+
+ jr PLY_AKM_PSES_SavePointerAndExit
+ ENDIF ;PLY_AKM_SE_HardwareSounds
+
+
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+ ;Shared code used by the "hardware only" and "software and hardware" part.
+ ;Reads the Retrig flag, the Hardware Envelope, the possible noise, the hardware period,
+ ;and sets the volume to 16. The R7 sound channel is NOT modified.
+PLY_AKM_PSES_Shared_ReadRetrigHardwareEnvPeriodNoise:
+ ;Retrig?
+ rra
+ IFDEF PLY_AKM_SE_Retrig ;CONFIG SPECIFIC
+ jr nc,PLY_AKM_PSES_H_AfterRetrig
+ ld d,a
+ ld a,255
+ ld (PLY_AKM_SetReg13Old + PLY_AKM_Offset1b),a
+ ld a,d
+PLY_AKM_PSES_H_AfterRetrig:
+ ENDIF ;PLY_AKM_SE_Retrig
+
+ ;The hardware envelope can be set (8-15).
+ and %111
+ add a,8
+ ld (PLY_AKM_SetReg13 + PLY_AKM_Offset1b),a
+
+ ;Noise?
+ IFDEF PLY_AKM_SE_HardwareNoise ;CONFIG SPECIFIC. B not needed after, we can put it in the condition too.
+ rl b
+ call PLY_AKM_PSES_ReadNoiseIfNeededAndOpenOrCloseNoiseChannel
+ ELSE
+ set 5,c ;No noise in compilation, so stops the noise.
+ ENDIF ;PLY_AKM_SE_HardwareNoise
+
+ ;Reads the hardware period.
+ call PLY_AKM_PSES_ReadHardwarePeriod
+
+ ;Sets the volume to "hardware". It still may be decreased.
+ ld a,16
+ jp PLY_AKM_PSES_ManageVolumeFromA_Hard
+ ENDIF ;PLY_CFG_UseHardwareSounds
+
+
+ IFDEF PLY_AKM_SE_Noise
+;If asked to, reads the noise pointed by HL, increases HL, and opens the noise channel. If no noise, closes the noise channel.
+;IN: Carry = true if noise to read. False if no noise, so the noise channel must be closed.
+; HL = the data.
+PLY_AKM_PSES_ReadNoiseIfNeededAndOpenOrCloseNoiseChannel:
+ jr c,PLY_AKM_PSES_ReadNoiseAndOpenNoiseChannel_OpenNoise
+ ;No noise, so closes the noise channel.
+ set 5,c
+ ret
+PLY_AKM_PSES_ReadNoiseAndOpenNoiseChannel_OpenNoise:
+ ;Reads the noise.
+ ld a,(hl)
+ ld (PLY_AKM_NoiseRegister),a
+ inc hl
+
+ ;Opens noise channel.
+ res 5,c
+ ret
+ ENDIF ;PLY_AKM_SE_Noise
+
+ IFDEF PLY_CFG_UseHardwareSounds ;CONFIG SPECIFIC
+;Reads the hardware period from HL and sets the R11/R12 registers. HL is incremented of 2.
+PLY_AKM_PSES_ReadHardwarePeriod:
+ ld a,(hl)
+ ld (PLY_AKM_Reg11),a
+ inc hl
+ ld a,(hl)
+ ld (PLY_AKM_Reg12),a
+ inc hl
+ ret
+ ENDIF ;PLY_CFG_UseHardwareSounds
+
+;Reads the software period from HL and sets the period registers thanks to IY. HL is incremented of 2.
+PLY_AKM_PSES_ReadSoftwarePeriod:
+ ld a,(hl)
+ ld (iy + PLY_AKM_Registers_OffsetSoftwarePeriodLSB),a
+ inc hl
+ ld a,(hl)
+ ld (iy + PLY_AKM_Registers_OffsetSoftwarePeriodMSB),a
+ inc hl
+ ret
+
+ IFDEF PLY_AKM_SE_VolumeSoft ;CONFIG SPECIFIC
+;Reads the volume in A, decreases it from the inverted volume of the channel, and sets the volume via IY.
+;IN: A = volume, from 0 to 15 (no hardware envelope).
+PLY_AKM_PSES_ManageVolumeFromA_Filter4Bits:
+ and %1111
+ ENDIF ;PLY_AKM_SE_VolumeSoft
+ IFDEF PLY_AKM_SE_VolumeSoftOrHard ;CONFIG SPECIFIC
+;After the filtering. Useful for hardware sound (volume has been forced to 16).
+PLY_AKM_PSES_ManageVolumeFromA_Hard:
+ ;Decreases the volume, checks the limit.
+ sub (ix + PLY_AKM_SoundEffectData_OffsetInvertedVolume)
+ jr nc,PLY_AKM_PSES_MVFA_NoOverflow
+ xor a
+PLY_AKM_PSES_MVFA_NoOverflow:
+ ld (iy + PLY_AKM_Registers_OffsetVolume),a
+ ret
+ ENDIF ;PLY_AKM_SE_VolumeSoftOrHard
+
+
+ ;The data for RAM player. For ROM player, it is declared in the main player.
+ IFNDEF PLY_AKM_Rom
+;The data of the Channels MUST be consecutive.
+PLY_AKM_Channel1_SoundEffectData:
+dkws (void):
+ dw 0 ;Points to the sound effect for the track 1, or 0 if not playing.
+PLY_AKM_Channel1_SoundEffectInvertedVolume:
+dkwe (void):
+dkbs (void):
+ db 0 ;Inverted volume.
+PLY_AKM_Channel1_SoundEffectCurrentStep:
+ db 0 ;Current step (>=0).
+PLY_AKM_Channel1_SoundEffectSpeed:
+ db 0 ;Speed (>=0).
+ ds 3,0 ;Padding.
+dkbe (void):
+PLY_AKM_Channel_SoundEffectDataSize: equ $ - PLY_AKM_Channel1_SoundEffectData
+
+dkbs (void):
+PLY_AKM_Channel2_SoundEffectData:
+ ds PLY_AKM_Channel_SoundEffectDataSize, 0
+PLY_AKM_Channel3_SoundEffectData:
+ ds PLY_AKM_Channel_SoundEffectDataSize, 0
+dkbe (void):
+
+ ;Checks that the pointers are consecutive.
+ assert (PLY_AKM_Channel1_SoundEffectData + PLY_AKM_Channel_SoundEffectDataSize) == PLY_AKM_Channel2_SoundEffectData
+ assert (PLY_AKM_Channel2_SoundEffectData + PLY_AKM_Channel_SoundEffectDataSize) == PLY_AKM_Channel3_SoundEffectData
+
+ ENDIF ;PLY_AKM_Rom
+
+;Offset from the beginning of the data, to reach the inverted volume.
+PLY_AKM_SoundEffectData_OffsetInvertedVolume: equ PLY_AKM_Channel1_SoundEffectInvertedVolume - PLY_AKM_Channel1_SoundEffectData
+PLY_AKM_SoundEffectData_OffsetCurrentStep: equ PLY_AKM_Channel1_SoundEffectCurrentStep - PLY_AKM_Channel1_SoundEffectData
+PLY_AKM_SoundEffectData_OffsetSpeed: equ PLY_AKM_Channel1_SoundEffectSpeed - PLY_AKM_Channel1_SoundEffectData \ No newline at end of file
diff --git a/src/mplayer/akm/README.md b/src/mplayer/akm/README.md
new file mode 100644
index 0000000..4b4fde0
--- /dev/null
+++ b/src/mplayer/akm/README.md
@@ -0,0 +1,10 @@
+# Arkos 2 AKM Player
+
+Configured to use ROM and default options expecting a 222 bytes buffer starting
+at 0xc000.
+
+## Local changes
+
+ - A new function `PLY_AKM_IsDoundEffectOn` was added; this is used by the
+ priority based sound effects.
+
diff --git a/src/mplayer/akm/akm_ubox.asm b/src/mplayer/akm/akm_ubox.asm
new file mode 100644
index 0000000..cbbf8af
--- /dev/null
+++ b/src/mplayer/akm/akm_ubox.asm
@@ -0,0 +1,27 @@
+
+; these are common
+PLY_AKM_HARDWARE_MSX = 1
+PLY_AKM_MANAGE_SOUND_EFFECTS = 1
+PLY_AKM_Rom = 1
+PLY_AKM_ROM_Buffer = #c000
+
+include "PlayerAkm.asm"
+
+; IN: L = channel
+; OUT: L = 0 if is not on
+PLY_AKM_IsSoundEffectOnDisarkGenerateExternalLabel:
+PLY_AKM_IsSoundEffectOn:
+ ld a,l
+ add a,a
+ add a,a
+ add a,a
+ ld c,a
+ ld b,0
+ ld hl,PLY_AKM_Channel1_SoundEffectData
+ add hl,bc
+ ld a,(hl)
+ inc hl
+ or (hl)
+ ld l,a
+ ret
+
diff --git a/src/mplayer/mplayer_init.z80 b/src/mplayer/mplayer_init.z80
new file mode 100644
index 0000000..5f30b91
--- /dev/null
+++ b/src/mplayer/mplayer_init.z80
@@ -0,0 +1,13 @@
+.globl _mplayer_init
+
+.globl _PLY_AKM_INIT
+
+_mplayer_init::
+ ld ix, #2
+ add ix, sp
+
+ ld l, 0 (ix)
+ ld h, 1 (ix)
+ ld a, 2 (ix)
+
+ jp _PLY_AKM_INIT
diff --git a/src/mplayer/mplayer_init_effects.z80 b/src/mplayer/mplayer_init_effects.z80
new file mode 100644
index 0000000..132bf3e
--- /dev/null
+++ b/src/mplayer/mplayer_init_effects.z80
@@ -0,0 +1,16 @@
+.module mplayer
+
+.globl _mplayer_init_effects
+.globl mplayer_current_efx
+
+.globl _PLY_AKM_INITSOUNDEFFECTS
+
+_mplayer_init_effects::
+ xor a
+ ld (mplayer_current_efx), a
+ jp _PLY_AKM_INITSOUNDEFFECTS
+
+.area _DATA
+
+mplayer_current_efx: .ds 1
+
diff --git a/src/mplayer/mplayer_is_sound_effect_on.z80 b/src/mplayer/mplayer_is_sound_effect_on.z80
new file mode 100644
index 0000000..67ede22
--- /dev/null
+++ b/src/mplayer/mplayer_is_sound_effect_on.z80
@@ -0,0 +1,9 @@
+.module mplayer
+
+.globl _mplayer_is_sound_effect_on
+
+.globl _PLY_AKM_ISSOUNDEFFECTON
+
+_mplayer_is_sound_effect_on::
+ jp _PLY_AKM_ISSOUNDEFFECTON
+
diff --git a/src/mplayer/mplayer_play.z80 b/src/mplayer/mplayer_play.z80
new file mode 100644
index 0000000..8516733
--- /dev/null
+++ b/src/mplayer/mplayer_play.z80
@@ -0,0 +1,9 @@
+.module mplayer
+
+.globl _mplayer_play
+
+.globl _PLY_AKM_PLAY
+
+_mplayer_play::
+ jp _PLY_AKM_PLAY
+
diff --git a/src/mplayer/mplayer_play_effect.z80 b/src/mplayer/mplayer_play_effect.z80
new file mode 100644
index 0000000..adf41b9
--- /dev/null
+++ b/src/mplayer/mplayer_play_effect.z80
@@ -0,0 +1,18 @@
+.module mplayer
+
+.globl _mplayer_play_effect
+
+.globl _PLY_AKM_PLAYSOUNDEFFECT
+
+_mplayer_play_effect::
+ ld hl, #2
+ add hl, sp
+
+ ld a, (hl)
+ inc hl
+ ld c, (hl)
+ inc hl
+ ld b, (hl)
+
+ jp _PLY_AKM_PLAYSOUNDEFFECT
+
diff --git a/src/mplayer/mplayer_play_effect_p.z80 b/src/mplayer/mplayer_play_effect_p.z80
new file mode 100644
index 0000000..8efd705
--- /dev/null
+++ b/src/mplayer/mplayer_play_effect_p.z80
@@ -0,0 +1,45 @@
+.module mplayer
+
+.globl _mplayer_play_effect_p
+.globl _mplayer_is_sound_effect_on
+.globl mplayer_current_efx
+
+.globl _PLY_AKM_PLAYSOUNDEFFECT
+
+_mplayer_play_effect_p::
+ ld hl, #2
+ add hl, sp
+
+ ld e, (hl)
+ inc hl
+ ld c, (hl)
+ inc hl
+ ld b, (hl)
+
+ ; e effect no
+ ; bc: channel and volume
+ push bc
+ push de
+ ld l, c
+ call _mplayer_is_sound_effect_on
+
+ ld a, l
+ or a
+ pop de
+ pop bc
+ jr z, play_efx
+
+ ld a, (mplayer_current_efx)
+ ; comment out following line if you don't want to
+ ; replace current sound if is the same effect type
+ dec a
+ cp e
+ ret nc
+
+play_efx:
+ ; all good, play the effect
+ ld a, e
+ ld (mplayer_current_efx), a
+
+ jp _PLY_AKM_PLAYSOUNDEFFECT
+
diff --git a/src/mplayer/mplayer_stop.z80 b/src/mplayer/mplayer_stop.z80
new file mode 100644
index 0000000..7025af6
--- /dev/null
+++ b/src/mplayer/mplayer_stop.z80
@@ -0,0 +1,9 @@
+.module mplayer
+
+.globl _mplayer_stop
+
+.globl _PLY_AKM_STOP
+
+_mplayer_stop::
+ jp _PLY_AKM_STOP
+
diff --git a/src/mplayer/mplayer_stop_effect_channel.z80 b/src/mplayer/mplayer_stop_effect_channel.z80
new file mode 100644
index 0000000..82eb5d7
--- /dev/null
+++ b/src/mplayer/mplayer_stop_effect_channel.z80
@@ -0,0 +1,10 @@
+.module mplayer
+
+.globl _mplayer_stop_effect_channel
+
+.globl _PLY_AKM_STOPSOUNDEFFECTFROMCHANNEL
+
+_mplayer_stop_effect_channel::
+ ld a, l
+ jp _PLY_AKM_STOPSOUNDEFFECTFROMCHANNEL
+
diff --git a/src/spman/Makefile b/src/spman/Makefile
new file mode 100644
index 0000000..b2bc5df
--- /dev/null
+++ b/src/spman/Makefile
@@ -0,0 +1,20 @@
+LIB=../../lib/spman.lib
+all: $(LIB)
+
+CC=sdcc
+AR=sdar
+CFLAGS=-mz80 --Werror -I../../include --fsigned-char --std-sdcc99 --opt-code-speed
+
+SOURCES=$(wildcard *.c)
+OBJS=$(patsubst %.c,%.rel,$(SOURCES))
+
+$(LIB): $(OBJS)
+ $(AR) -rcD $(LIB) $(OBJS)
+
+%.rel: %.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -c $<
+
+.PHONY: clean
+clean:
+ rm -f $(OBJS) $(LIB)
+
diff --git a/src/spman/spman.c b/src/spman/spman.c
new file mode 100644
index 0000000..0c7d7d1
--- /dev/null
+++ b/src/spman/spman.c
@@ -0,0 +1,118 @@
+#include <stdint.h>
+#include <string.h>
+
+#include "spman.h"
+
+#define SPMAN_PAT_UNUSED 0xff
+#define SPMAN_MAX_SPRITES 32
+#define SPMAN_MAX_PATTERNS 64
+
+uint8_t sp_last_sprite, sp_last_fixed_sprite, sp_idx;
+struct sprite_attr sp_fixed[SPMAN_MAX_SPRITES];
+struct sprite_attr sp_buffer[SPMAN_MAX_SPRITES * 2];
+
+uint8_t sp_pat_map[SPMAN_MAX_PATTERNS];
+uint8_t sp_last_pat;
+
+void spman_init()
+{
+ sp_last_pat = 0;
+ memset(sp_pat_map, SPMAN_PAT_UNUSED, SPMAN_MAX_PATTERNS);
+ spman_sprite_flush();
+}
+
+uint8_t spman_alloc_pat(uint8_t type, uint8_t *data, uint8_t len, uint8_t flip)
+{
+ uint8_t i;
+
+ if (sp_pat_map[type] == SPMAN_PAT_UNUSED)
+ {
+ sp_pat_map[type] = sp_last_pat;
+ for (i = 0; i < len; ++i)
+ {
+ if (flip)
+ ubox_set_sprite_pat16_flip(data, sp_last_pat);
+ else
+ ubox_set_sprite_pat16(data, sp_last_pat);
+
+ data += 32;
+ sp_last_pat++;
+ }
+#ifdef SPMAN_DEBUG
+ if (sp_last_pat > 63)
+ ubox_set_colors(15, 0, 0);
+#endif
+ }
+
+ return sp_pat_map[type] * 4;
+}
+
+void spman_sprite_flush()
+{
+ sp_last_fixed_sprite = 0;
+ sp_last_sprite = 0;
+}
+
+void spman_alloc_fixed_sprite(struct sprite_attr *sp)
+{
+#ifdef SPMAN_DEBUG
+ if (sp_last_fixed_sprite + sp_last_sprite > 30)
+ {
+ ubox_set_colors(15, 0, 0);
+ return;
+ }
+#endif
+ memcpy(&sp_fixed[sp_last_fixed_sprite++], sp, 4);
+}
+
+void spman_alloc_sprite(struct sprite_attr *sp)
+{
+#ifdef SPMAN_DEBUG
+ if (sp_last_fixed_sprite + sp_last_sprite > 30)
+ {
+ ubox_set_colors(15, 0, 0);
+ return;
+ }
+#endif
+ memcpy(&sp_buffer[sp_last_sprite++], sp, 4);
+}
+
+static const struct sprite_attr hide = { 208, 0, 0, 0 };
+
+static uint8_t *p;
+
+void spman_update()
+{
+ p = (uint8_t*) 0x1b00;
+
+ if (sp_last_sprite)
+ {
+ memcpy(&sp_buffer[sp_last_sprite], sp_buffer, sp_last_sprite * 4);
+
+ if (sp_last_sprite > 2)
+ sp_idx += 2;
+
+ if (sp_idx >= sp_last_sprite)
+ sp_idx -= sp_last_sprite;
+ }
+ else
+ sp_idx = 0;
+
+ memcpy(&sp_buffer[sp_idx + sp_last_sprite], &hide, 4);
+
+ ubox_wait_vsync();
+ if (sp_last_fixed_sprite)
+ {
+ ubox_write_vm(p, sp_last_fixed_sprite * 4, (uint8_t *)sp_fixed);
+ p += sp_last_fixed_sprite * 4;
+ }
+ ubox_write_vm(p, 4 + sp_last_sprite * 4, (uint8_t *)&sp_buffer[sp_idx]);
+
+ spman_sprite_flush();
+}
+
+void spman_hide_all_sprites()
+{
+ ubox_wait_vsync();
+ ubox_write_vm((uint8_t *)0x1b00, 4, (uint8_t *)hide);
+}
diff --git a/src/ubox/Makefile b/src/ubox/Makefile
new file mode 100644
index 0000000..a597090
--- /dev/null
+++ b/src/ubox/Makefile
@@ -0,0 +1,19 @@
+LIB=../../lib/ubox.lib
+all: $(LIB)
+
+AS=sdasz80
+AR=sdar
+
+SOURCES=$(wildcard ubox_*.z80)
+OBJS=$(patsubst %.z80,%.rel,$(SOURCES))
+
+$(LIB): $(OBJS)
+ $(AR) -rcD $(LIB) $(OBJS)
+
+%.rel: %.z80
+ $(AS) -o $<
+
+.PHONY: clean
+clean:
+ rm -f $(OBJS) $(LIB)
+
diff --git a/src/ubox/ubox_disable_screen.z80 b/src/ubox/ubox_disable_screen.z80
new file mode 100644
index 0000000..767f335
--- /dev/null
+++ b/src/ubox/ubox_disable_screen.z80
@@ -0,0 +1,7 @@
+.globl _ubox_disable_screen
+
+DISSCR = 0x0041
+
+_ubox_disable_screen::
+ jp DISSCR
+
diff --git a/src/ubox/ubox_enable_screen.z80 b/src/ubox/ubox_enable_screen.z80
new file mode 100644
index 0000000..8ab0361
--- /dev/null
+++ b/src/ubox/ubox_enable_screen.z80
@@ -0,0 +1,7 @@
+.globl _ubox_enable_screen
+
+ENASCR = 0x0044
+
+_ubox_enable_screen::
+ jp ENASCR
+
diff --git a/src/ubox/ubox_fill_screen.z80 b/src/ubox/ubox_fill_screen.z80
new file mode 100644
index 0000000..ed8c58f
--- /dev/null
+++ b/src/ubox/ubox_fill_screen.z80
@@ -0,0 +1,10 @@
+.globl _ubox_fill_screen
+
+FILVRM = 0x0056
+
+_ubox_fill_screen::
+ ld a, l
+ ld hl, #0x1800
+ ld bc, #768
+ jp FILVRM
+
diff --git a/src/ubox/ubox_get_tile.z80 b/src/ubox/ubox_get_tile.z80
new file mode 100644
index 0000000..49dcb30
--- /dev/null
+++ b/src/ubox/ubox_get_tile.z80
@@ -0,0 +1,28 @@
+.globl _ubox_get_tile
+
+RDVRM = 0x004a
+
+_ubox_get_tile::
+ ld hl, #2
+ add hl, sp
+
+ ld b, #0
+ ld c, (hl)
+ inc hl
+ ld l, (hl)
+
+ ld h, #0
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ add hl, bc
+
+ ld bc, #0x1800
+ add hl, bc
+
+ call RDVRM
+ ld l, a
+ ret
+
diff --git a/src/ubox/ubox_get_vsync_freq.z80 b/src/ubox/ubox_get_vsync_freq.z80
new file mode 100644
index 0000000..681fe67
--- /dev/null
+++ b/src/ubox/ubox_get_vsync_freq.z80
@@ -0,0 +1,9 @@
+.globl _ubox_get_vsync_freq
+
+_ubox_get_vsync_freq::
+ ld a, (#0x002b)
+ and #128
+ ld l, a
+ ret z
+ ld l, #1
+ ret
diff --git a/src/ubox/ubox_isr.z80 b/src/ubox/ubox_isr.z80
new file mode 100644
index 0000000..cceb2c6
--- /dev/null
+++ b/src/ubox/ubox_isr.z80
@@ -0,0 +1,79 @@
+.globl _ubox_init_isr
+.globl _ubox_tick
+.globl ubox_isr_wait_ticks
+.globl ubox_isr_wait_tick
+.globl ubox_usr_isr
+.globl ___sdcc_call_hl
+
+HTIMI_HOOK = 0xfd9f
+
+SCNCNT = 0xf3f6
+REPCNT = 0xf3f7
+
+_ubox_init_isr::
+ di
+ ld a, l
+ ld (ubox_isr_wait_ticks), a
+ xor a
+ ld (ubox_isr_wait_tick), a
+ ld (_ubox_tick), a
+ ld (ubox_usr_isr), a
+ ld (ubox_usr_isr+1), a
+ ld hl, #ubox_isr
+ ex de, hl
+ ld hl, #HTIMI_HOOK
+ ld a, #0xc3
+ ld (hl), a
+ inc hl
+ ld (hl), e
+ inc hl
+ ld (hl), d
+ ei
+ ret
+
+ubox_isr:
+ push af
+ push ix
+ push iy
+ push bc
+ push hl
+ push de
+
+ ; stop BIOS reading keyboard buffer
+ xor a
+ ld (SCNCNT), a
+ ld (REPCNT), a
+
+ ld hl, #ubox_isr_wait_tick
+ inc (hl)
+ inc hl
+ inc (hl)
+ inc hl
+ inc hl
+
+ ; check user isr, only MSB
+ ld a, (hl)
+ or a
+ jr z, no_user_isr
+
+ dec hl
+ ld l, (hl)
+ ld h, a
+ call ___sdcc_call_hl
+
+no_user_isr:
+ pop de
+ pop hl
+ pop bc
+ pop iy
+ pop ix
+ pop af
+ ret
+
+.area _DATA
+
+ubox_isr_wait_ticks: .ds 1
+ubox_isr_wait_tick: .ds 1
+_ubox_tick: .ds 1
+
+ubox_usr_isr: .ds 2
diff --git a/src/ubox/ubox_put_tile.z80 b/src/ubox/ubox_put_tile.z80
new file mode 100644
index 0000000..1bdf3ce
--- /dev/null
+++ b/src/ubox/ubox_put_tile.z80
@@ -0,0 +1,30 @@
+.globl _ubox_put_tile
+
+WRTVRM = 0x004d
+
+_ubox_put_tile::
+ ld hl, #2
+ add hl, sp
+
+ ld c, (hl)
+ inc hl
+ ld b, (hl)
+ inc hl
+
+ ld a, (hl)
+
+ ld h, #0
+ ld l, b
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ ld b, #0
+ add hl, bc
+
+ ld bc, #0x1800
+ add hl, bc
+
+ jp WRTVRM
+
diff --git a/src/ubox/ubox_read_ctl.z80 b/src/ubox/ubox_read_ctl.z80
new file mode 100644
index 0000000..91963c0
--- /dev/null
+++ b/src/ubox/ubox_read_ctl.z80
@@ -0,0 +1,100 @@
+.globl _ubox_read_ctl
+
+RDPSG = 0x0096
+WRTPSG = 0x0093
+SNSMAT = 0x0141
+
+_ubox_read_ctl::
+ ld a, l
+ or a
+ jr z, is_keyb
+ dec a
+ jr z, is_joy1
+ dec a
+ jr z, is_joy2
+
+ ld l, #0
+ ret
+
+is_joy2:
+ ; select
+ ld e, #0xcf
+ jr call_psg
+
+is_joy1:
+ ; select
+ ld e, #0x8f
+
+call_psg:
+ ld a, #0x0f
+ call WRTPSG
+
+ ld a, #0x0e
+
+ di
+ call RDPSG
+ ei
+ cpl
+ and #0x3f
+ ld e, a
+
+ ; button 2 (M)
+ ld a, #4
+ call SNSMAT
+ rra
+ rra
+ rra
+ ld a, #0x20
+ jr nc, joy_extra_m
+ xor a
+joy_extra_m:
+ or e
+ ld l, a
+ ret
+
+is_keyb:
+ ; button 2 (M)
+ ld a, #4
+ call SNSMAT
+ rra
+ rra
+ rra
+ ld e, #0x20
+ jr nc, is_keyb_dir
+
+ ld e, #0
+
+is_keyb_dir:
+ ; direction
+ ld a, #8
+ call SNSMAT
+ cpl
+ rrca
+ rrca
+ ld c, a
+
+ and #4
+ ld b, a
+ ; b has left
+
+ ld a, c
+ rrca
+ rrca
+ ld c, a
+
+ and #0x18
+ or b
+ ld b, a
+ ; added space and right
+
+ ld a, c
+ rrca
+ and #3
+ or b
+ ; added down and up
+
+ or e
+ ; added 2nd fire (M)
+
+ ld l, a
+ ret
diff --git a/src/ubox/ubox_read_keys.z80 b/src/ubox/ubox_read_keys.z80
new file mode 100644
index 0000000..d32f2de
--- /dev/null
+++ b/src/ubox/ubox_read_keys.z80
@@ -0,0 +1,10 @@
+.globl _ubox_read_keys
+
+SNSMAT = 0x0141
+
+_ubox_read_keys::
+ ld a, l
+ call SNSMAT
+ cpl
+ ld l, a
+ ret
diff --git a/src/ubox/ubox_read_vm.z80 b/src/ubox/ubox_read_vm.z80
new file mode 100644
index 0000000..7ef0f41
--- /dev/null
+++ b/src/ubox/ubox_read_vm.z80
@@ -0,0 +1,23 @@
+.globl _ubox_read_vm
+
+LDIRMV = 0x0059
+
+_ubox_read_vm::
+ ld hl, #2
+ add hl, sp
+
+ ld e, (hl)
+ inc hl
+ ld d, (hl)
+ inc hl
+ ld c, (hl)
+ inc hl
+ ld b, (hl)
+ inc hl
+ ld a, (hl)
+ inc hl
+ ld h, (hl)
+ ld l, a
+
+ jp LDIRMV
+
diff --git a/src/ubox/ubox_reset_tick.z80 b/src/ubox/ubox_reset_tick.z80
new file mode 100644
index 0000000..96bb1bf
--- /dev/null
+++ b/src/ubox/ubox_reset_tick.z80
@@ -0,0 +1,10 @@
+.globl _ubox_reset_tick
+.globl _ubox_tick
+
+_ubox_reset_tick::
+ xor a
+ di
+ ld (_ubox_tick), a
+ ei
+ ret
+
diff --git a/src/ubox/ubox_select_ctl.z80 b/src/ubox/ubox_select_ctl.z80
new file mode 100644
index 0000000..9685bec
--- /dev/null
+++ b/src/ubox/ubox_select_ctl.z80
@@ -0,0 +1,42 @@
+.globl _ubox_select_ctl
+
+GTTRIG = 0x00d8
+
+_ubox_select_ctl::
+ ld b, #3
+loop:
+ ld a, b
+ dec a
+ push bc
+ call GTTRIG
+ pop bc
+ or a
+ jr nz, trigger
+ djnz loop
+
+ ; 2nd button
+
+ ld b, #4
+ ld a, b
+ push bc
+ call GTTRIG
+ pop bc
+ or a
+ jr nz, trigger_b
+
+ dec b
+ ld a, b
+ push bc
+ call GTTRIG
+ pop bc
+ or a
+ jr nz, trigger_b
+
+ ld l, #0xff
+ ret
+trigger_b:
+ dec b
+trigger:
+ dec b
+ ld l, b
+ ret
diff --git a/src/ubox/ubox_set_colors.z80 b/src/ubox/ubox_set_colors.z80
new file mode 100644
index 0000000..b4638b8
--- /dev/null
+++ b/src/ubox/ubox_set_colors.z80
@@ -0,0 +1,23 @@
+.globl _ubox_set_colors
+
+CHGCLR = 0x0062
+FORCLR = 0xf3e9
+BAKCLR = 0xf3ea
+BDRCLR = 0xf3eb
+
+_ubox_set_colors::
+ ld hl, #2
+ add hl, sp
+
+ ld a, (hl)
+ inc hl
+ ld (FORCLR), a
+ ld a, (hl)
+ inc hl
+ ld (BAKCLR), a
+ ld a, (hl)
+ inc hl
+ ld (BDRCLR), a
+ call CHGCLR
+ ret
+
diff --git a/src/ubox/ubox_set_mode.z80 b/src/ubox/ubox_set_mode.z80
new file mode 100644
index 0000000..4635d34
--- /dev/null
+++ b/src/ubox/ubox_set_mode.z80
@@ -0,0 +1,8 @@
+.globl _ubox_set_mode
+
+CHGMOD = 0x005f
+
+_ubox_set_mode::
+ ld a, l
+ jp CHGMOD
+
diff --git a/src/ubox/ubox_set_sprite_attr.z80 b/src/ubox/ubox_set_sprite_attr.z80
new file mode 100644
index 0000000..fe573b6
--- /dev/null
+++ b/src/ubox/ubox_set_sprite_attr.z80
@@ -0,0 +1,29 @@
+.globl _ubox_set_sprite_attr
+.globl _ubox_write_vm
+
+_ubox_set_sprite_attr::
+ ld hl, #2
+ add hl, sp
+
+ ld e, (hl)
+ inc hl
+ ld d, (hl)
+ inc hl
+
+ ld l, (hl)
+ sla l
+ sla l
+ ld h, #0
+ ld bc, #0x1b00
+ add hl, bc
+
+ push de
+ ld bc, #4
+ push bc
+ push hl
+ call _ubox_write_vm
+ pop af
+ pop af
+ pop af
+ ret
+
diff --git a/src/ubox/ubox_set_sprite_pat16.z80 b/src/ubox/ubox_set_sprite_pat16.z80
new file mode 100644
index 0000000..4689267
--- /dev/null
+++ b/src/ubox/ubox_set_sprite_pat16.z80
@@ -0,0 +1,32 @@
+.globl _ubox_set_sprite_pat16
+.globl _ubox_write_vm
+
+_ubox_set_sprite_pat16::
+ ld hl, #2
+ add hl, sp
+
+ ld e, (hl)
+ inc hl
+ ld d, (hl)
+ inc hl
+
+ ld l, (hl)
+ ld h, #0
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ ld bc, #0x3800
+ add hl, bc
+
+ push de
+ ld bc, #32
+ push bc
+ push hl
+ call _ubox_write_vm
+ pop af
+ pop af
+ pop af
+ ret
+
diff --git a/src/ubox/ubox_set_sprite_pat16_flip.z80 b/src/ubox/ubox_set_sprite_pat16_flip.z80
new file mode 100644
index 0000000..bb960f8
--- /dev/null
+++ b/src/ubox/ubox_set_sprite_pat16_flip.z80
@@ -0,0 +1,63 @@
+.globl _ubox_set_sprite_pat16_flip
+
+WRTVRM = 0x004d
+
+_ubox_set_sprite_pat16_flip::
+ ld hl, #2
+ add hl, sp
+
+ ld e, (hl)
+ inc hl
+ ld d, (hl)
+ inc hl
+
+ ld l, (hl)
+ ld h, #0
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ ld bc, #0x3800
+ add hl, bc
+
+ push de
+ ld bc, #16
+ ex de, hl
+ add hl, bc
+ ex de, hl
+ call flip
+
+ pop de
+ call flip
+
+ ret
+
+flip:
+ ld b, #16
+flip0:
+ call flip_and_copy
+ inc hl
+ inc de
+ djnz flip0
+ ret
+
+flip_and_copy:
+ ld a, (de)
+ ld c, a
+ rlca
+ rlca
+ xor c
+ and #0xaa
+ xor c
+ ld c, a
+ rlca
+ rlca
+ rlca
+ rrc c
+ xor c
+ and #0x66
+ xor c
+
+ jp WRTVRM
+
diff --git a/src/ubox/ubox_set_sprite_pat8.z80 b/src/ubox/ubox_set_sprite_pat8.z80
new file mode 100644
index 0000000..11b0205
--- /dev/null
+++ b/src/ubox/ubox_set_sprite_pat8.z80
@@ -0,0 +1,30 @@
+.globl _ubox_set_sprite_pat8
+.globl _ubox_write_vm
+
+_ubox_set_sprite_pat8::
+ ld hl, #2
+ add hl, sp
+
+ ld e, (hl)
+ inc hl
+ ld d, (hl)
+ inc hl
+
+ ld l, (hl)
+ ld h, #0
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ ld bc, #0x3800
+ add hl, bc
+
+ push de
+ ld bc, #8
+ push bc
+ push hl
+ call _ubox_write_vm
+ pop af
+ pop af
+ pop af
+ ret
+
diff --git a/src/ubox/ubox_set_sprite_pat8_flip.z80 b/src/ubox/ubox_set_sprite_pat8_flip.z80
new file mode 100644
index 0000000..a3f8d1d
--- /dev/null
+++ b/src/ubox/ubox_set_sprite_pat8_flip.z80
@@ -0,0 +1,48 @@
+.globl _ubox_set_sprite_pat8_flip
+
+WRTVRM = 0x004d
+
+_ubox_set_sprite_pat8_flip::
+ ld hl, #2
+ add hl, sp
+
+ ld e, (hl)
+ inc hl
+ ld d, (hl)
+ inc hl
+
+ ld l, (hl)
+ ld h, #0
+ add hl, hl
+ add hl, hl
+ add hl, hl
+ ld bc, #0x3800
+ add hl, bc
+
+ ld b, #8
+flip0:
+ call flip_and_copy
+ inc hl
+ inc de
+ djnz flip0
+ ret
+
+flip_and_copy:
+ ld a, (de)
+ ld c, a
+ rlca
+ rlca
+ xor c
+ and #0xaa
+ xor c
+ ld c, a
+ rlca
+ rlca
+ rlca
+ rrc c
+ xor c
+ and #0x66
+ xor c
+
+ jp WRTVRM
+
diff --git a/src/ubox/ubox_set_tiles.z80 b/src/ubox/ubox_set_tiles.z80
new file mode 100644
index 0000000..fbb2ff9
--- /dev/null
+++ b/src/ubox/ubox_set_tiles.z80
@@ -0,0 +1,21 @@
+.globl _ubox_set_tiles
+
+LDIRVM = 0x005c
+
+_ubox_set_tiles::
+ ld de, #0
+ ld bc, #256 * 8
+ push hl
+ call LDIRVM
+ pop hl
+
+ ld de, #256 * 8
+ ld bc, #256 * 8
+ push hl
+ call LDIRVM
+ pop hl
+
+ ld de, #256 * 8 * 2
+ ld bc, #256 * 8
+ jp LDIRVM
+
diff --git a/src/ubox/ubox_set_tiles_colors.z80 b/src/ubox/ubox_set_tiles_colors.z80
new file mode 100644
index 0000000..8ba2609
--- /dev/null
+++ b/src/ubox/ubox_set_tiles_colors.z80
@@ -0,0 +1,21 @@
+.globl _ubox_set_tiles_colors
+
+LDIRVM = 0x005c
+
+_ubox_set_tiles_colors::
+ ld de, #0x2000
+ ld bc, #256 * 8
+ push hl
+ call LDIRVM
+ pop hl
+
+ ld de, #0x2000 + 256 * 8
+ ld bc, #256 * 8
+ push hl
+ call LDIRVM
+ pop hl
+
+ ld de, #0x2000 + 256 * 8 * 2
+ ld bc, #256 * 8
+ jp LDIRVM
+
diff --git a/src/ubox/ubox_set_user_isr.z80 b/src/ubox/ubox_set_user_isr.z80
new file mode 100644
index 0000000..5722909
--- /dev/null
+++ b/src/ubox/ubox_set_user_isr.z80
@@ -0,0 +1,8 @@
+.globl _ubox_set_user_isr
+.globl ubox_usr_isr
+
+_ubox_set_user_isr::
+ di
+ ld (ubox_usr_isr), hl
+ ei
+ ret
diff --git a/src/ubox/ubox_wait.z80 b/src/ubox/ubox_wait.z80
new file mode 100644
index 0000000..49fd0be
--- /dev/null
+++ b/src/ubox/ubox_wait.z80
@@ -0,0 +1,23 @@
+.globl _ubox_wait
+.globl ubox_isr_wait_ticks
+.globl ubox_isr_wait_tick
+
+_ubox_wait::
+ ld hl, #ubox_isr_wait_tick
+ ld a, (ubox_isr_wait_ticks)
+ ld c, a
+
+wait_loop:
+ ld a, (hl)
+ cp c
+ jr nc, wait_done
+ halt
+ jr wait_loop
+
+wait_done:
+ xor a
+ di
+ ld (hl), a
+ ei
+ ret
+
diff --git a/src/ubox/ubox_wait_for.z80 b/src/ubox/ubox_wait_for.z80
new file mode 100644
index 0000000..5cc4649
--- /dev/null
+++ b/src/ubox/ubox_wait_for.z80
@@ -0,0 +1,14 @@
+.globl _ubox_wait_for
+.globl _ubox_wait
+.globl ubox_isr_wait_ticks
+.globl ubox_isr_wait_tick
+
+_ubox_wait_for::
+ ld b, l
+wait_for_loop:
+ push bc
+ call _ubox_wait
+ pop bc
+ djnz wait_for_loop
+ ret
+
diff --git a/src/ubox/ubox_write_vm.z80 b/src/ubox/ubox_write_vm.z80
new file mode 100644
index 0000000..3d6606b
--- /dev/null
+++ b/src/ubox/ubox_write_vm.z80
@@ -0,0 +1,23 @@
+.globl _ubox_write_vm
+
+LDIRVM = 0x005c
+
+_ubox_write_vm::
+ ld hl, #2
+ add hl, sp
+
+ ld e, (hl)
+ inc hl
+ ld d, (hl)
+ inc hl
+ ld c, (hl)
+ inc hl
+ ld b, (hl)
+ inc hl
+ ld a, (hl)
+ inc hl
+ ld h, (hl)
+ ld l, a
+
+ jp LDIRVM
+
diff --git a/src/ubox/ubox_wvdp.z80 b/src/ubox/ubox_wvdp.z80
new file mode 100644
index 0000000..90dfc86
--- /dev/null
+++ b/src/ubox/ubox_wvdp.z80
@@ -0,0 +1,14 @@
+.globl _ubox_wvdp
+
+WRITEVDP = 0x0047
+
+_ubox_wvdp::
+ ld hl, #2
+ add hl, sp
+
+ ld c, (hl)
+ inc hl
+ ld b, (hl)
+ call WRITEVDP
+ ret
+
diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644
index 0000000..6223252
--- /dev/null
+++ b/tools/Makefile
@@ -0,0 +1,17 @@
+BIN=../bin/hex2bin ../bin/rasm
+
+all: $(BIN)
+
+../bin/hex2bin:
+ mkdir -p hex2bin-2.0/bin
+ make -C hex2bin-2.0
+ cp hex2bin-2.0/bin/hex2bin ../bin
+ make -C hex2bin-2.0 cleanall
+
+../bin/rasm:
+ make -C rasm
+
+.PHONY: all clean
+clean:
+ make -C hex2bin-2.0 cleanall
+ make -C rasm clean
diff --git a/tools/chksize b/tools/chksize
new file mode 100755
index 0000000..f7c2d41
--- /dev/null
+++ b/tools/chksize
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+
+import sys
+
+TOP_MEM = 0xc000 - 256 # video memory - stack
+
+
+def main():
+ if len(sys.argv) != 4:
+ sys.exit("usage: %s code_limit_hex data_limit_hex filename.map" %
+ sys.argv[0])
+
+ sizes = {"CODE": 0, "DATA": 0}
+
+ with open(sys.argv[3], "r") as fd:
+ for line in fd.readlines():
+ for seg in sizes.keys():
+ if "l__%s" % seg in line:
+ sizes[seg] = int(line.split()[0], base=16)
+
+ print("\nROM: %(CODE)05d bytes\nRAM: %(DATA)05d bytes\n" % sizes)
+
+ if sizes["CODE"] > int(sys.argv[1], 16):
+ sys.exit("ROM is over the limit")
+ if sizes["DATA"] > int(sys.argv[2], 16):
+ sys.exit("RAM is over the limit")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/hdoc.py b/tools/hdoc.py
new file mode 100755
index 0000000..3a32eb3
--- /dev/null
+++ b/tools/hdoc.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python3
+
+import sys
+import re
+from subprocess import Popen, PIPE
+from argparse import ArgumentParser
+
+__version__ = "0.1"
+
+RE_FN = re.compile("[^\s]+\s+([^\(]+)\(")
+RE_STRUCT = re.compile("(struct\s+[^{]+)\s*{")
+RE_DEFINE = re.compile("#define\s+([^(]+)\(")
+RE_DOC = re.compile("\s+\*\s?(.*\n)")
+RE_SEC = re.compile("//\s+@(.*)\n")
+RE_SECDOC = re.compile("//\s?(.*\n)")
+
+
+def main():
+
+ parser = ArgumentParser(description="Include docs to markdown",
+ epilog="Copyright (C) 2020 Juan J Martinez <jjm@usebox.net>",
+ )
+
+ parser.add_argument("--version", action="version",
+ version="%(prog)s " + __version__)
+ parser.add_argument("--doc-version", dest="docver", default=None,
+ help="Use this as version instead of git tag/commit")
+ parser.add_argument("--header", dest="header", default=None,
+ help="Add this file at the begining of the document")
+ parser.add_argument("--footer", dest="footer", default=None,
+ help="Add this file at the end of the document")
+ parser.add_argument("title", help="Title of the resulting document")
+
+ args = parser.parse_args()
+
+ if args.docver is None:
+ proc = Popen(["git", "describe", "--abbrev=0", "--tags"],
+ stdout=PIPE, stderr=PIPE)
+ out, err = proc.communicate()
+ if proc.returncode != 0:
+ proc = Popen(["git", "rev-parse", "--short", "HEAD"],
+ stdout=PIPE, stderr=PIPE)
+ out, err = proc.communicate()
+ out = b"git-" + out
+
+ docver = out.decode("utf-8").strip()
+ else:
+ docver = args.docver
+
+ data = sys.stdin.readlines()
+ ref = {}
+ section = None
+ sections = {}
+ while data:
+ line = data.pop(0)
+
+ # section comment
+ if line.strip().startswith("//"):
+ g = RE_SEC.search(line)
+ if g:
+ section = g.group(1)
+ doclines = []
+ while True:
+ line = data.pop(0)
+ if not line.strip():
+ break
+ doclines.append(line)
+
+ doc = ""
+ for l in doclines:
+ g = RE_SECDOC.match(l)
+ if g:
+ doc += g.group(1)
+
+ sections[section] = doc
+
+ # begin fn/struct comment
+ if line.strip() == "/**":
+ doclines = []
+ while True:
+ line = data.pop(0)
+ if line.strip() == "*/":
+ break
+ doclines.append(line)
+
+ line = data.pop(0)
+ g = RE_DEFINE.search(line)
+ if g is None:
+ g = RE_STRUCT.search(line)
+ if g is None:
+ g = RE_FN.search(line)
+ if g is None:
+ continue
+ else:
+ fn = line.strip()
+ else:
+ fn = line
+ while True:
+ line = data.pop(0)
+ fn += line
+ if line.strip() == "};":
+ break
+ else:
+ fn = line
+ while True:
+ line = data.pop(0)
+ fn += line
+ if not line.strip().endswith("\\"):
+ fn = fn.rstrip()
+ break
+
+ name = g.group(1).strip()
+ anchor = name.replace(" ", "-")
+
+ doc = ""
+ for l in doclines:
+ g = RE_DOC.match(l)
+ if g:
+ doc += g.group(1)
+ ref[name] = {"name": name, "anchor": anchor,
+ "fn": fn, "doc": doc, "section": section}
+
+ print("""\
+---
+title: '{title}'
+subtitle: 'Version {version}'
+...""".format(title=args.title, version=docver))
+
+ if args.header:
+ with open(args.header, "rt") as fd:
+ print(fd.read())
+
+ csec = None
+ for k in sorted(ref.keys(), key=lambda k: (ref[k]["section"], k)):
+ v = ref[k]
+ if csec != v["section"]:
+ csec = v["section"]
+ print("## %s\n" % csec)
+ if csec in sections:
+ print(sections[csec])
+
+ print("""\
+### {name}
+```c
+{fn}
+```
+
+{doc}
+""".format(**v))
+
+ if args.footer:
+ with open(args.footer, "rt") as fd:
+ print(fd.read())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/hex2bin-2.0/Makefile b/tools/hex2bin-2.0/Makefile
new file mode 100755
index 0000000..c8ef10b
--- /dev/null
+++ b/tools/hex2bin-2.0/Makefile
@@ -0,0 +1,42 @@
+# Makefile hex2bin/mot2bin
+SRCDIR = src
+BINDIR = bin
+OBJDIR = obj
+TGTDIR = $(BINDIR)
+B_SRCFILES= $(foreach F, hex2bin.c common.c libcrc.c binary.c, $(SRCDIR)/$(F))
+B_OBJFILES= $(foreach F, hex2bin.o common.o libcrc.o binary.o, $(OBJDIR)/$(F))
+M_SRCFILES= $(foreach F, mot2bin.c common.c libcrc.c binary.c, $(SRCDIR)/$(F))
+M_OBJFILES= $(foreach F, mot2bin.o common.o libcrc.o binary.o, $(OBJDIR)/$(F))
+
+# For generating documentation (hex2bin.1, select the second line)
+# -- You will require pod2man installed for this to work
+TGT_FILES = $(foreach F, hex2bin mot2bin, $(TGTDIR)/$(F))
+#TGT_FILES = $(foreach F, hex2bin mot2bin hex2bin.1, $(TGTDIR)/$(F))
+
+CPFLAGS = -std=gnu99 -O3 -fsigned-char -Wall -pedantic
+# Compile
+all: objectdir $(TGT_FILES)
+
+$(OBJDIR)/%.o: $(SRCDIR)/%.c
+ gcc -c $(CPFLAGS) $< -o $@
+
+objectdir:
+ @echo "Creating directory $(OBJDIR)..."
+ mkdir -p $(OBJDIR)
+
+$(TGTDIR)/hex2bin.1: $(SRCDIR)/hex2bin.pod
+ pod2man $(SRCDIR)/hex2bin.pod > $(TGTDIR)/hex2bin.1
+
+$(TGTDIR)/hex2bin: $(B_OBJFILES)
+ gcc $(CPFLAGS) -o $(TGTDIR)/hex2bin $(B_OBJFILES)
+
+$(TGTDIR)/mot2bin: $(M_OBJFILES)
+ gcc $(CPFLAGS) -o $(TGTDIR)/mot2bin $(M_OBJFILES)
+
+clean:
+ @echo "Removing objects directory $(OBJDIR)/ ..."
+ @rm -rf $(OBJDIR)
+
+cleanall: clean
+ @echo "Removing binary files in $(BINDIR)/ ..."
+ @rm -f $(BINDIR)/*
diff --git a/tools/hex2bin-2.0/doc/CRC list.txt b/tools/hex2bin-2.0/doc/CRC list.txt
new file mode 100644
index 0000000..8549799
--- /dev/null
+++ b/tools/hex2bin-2.0/doc/CRC list.txt
@@ -0,0 +1,542 @@
+http://regregex.bbcmicro.net/crc-catalogue.htm
+
+CRC-8
+07
+<crc-8-atm>
+ Name : "CRC-8"
+ Width : 8
+ Poly : 07
+ Init : 00
+ RefIn : False
+ RefOut : False
+ XorOut : 00
+ Check : F4
+
+<crc-8-itu>
+ Name : "CRC-8/ITU"
+ Width : 8
+ Poly : 07
+ Init : 00
+ RefIn : False
+ RefOut : False
+ XorOut : 55
+ Check : A1
+
+<crc-8-rohc>
+ Name : "CRC-8/ROHC"
+ Width : 8
+ Poly : 07
+ Init : FF
+ RefIn : True
+ RefOut : True
+ XorOut : 0
+ Check : D0
+
+39
+<crc-8-darc>
+ Name : "CRC-8/DARC"
+ Width : 8
+ Poly : 39
+ Init : 00
+ RefIn : True
+ RefOut : True
+ XorOut : 00
+ Check : 15
+
+1D
+<crc-8-icode>
+ Name : "CRC-8/I-CODE"
+ Width : 8
+ Poly : 1D
+ Init : FD
+ RefIn : False
+ RefOut : False
+ XorOut : 00
+ Check : 7E
+
+<crc-8-j1850>
+ Name : "CRC-8/J1850" (new entry)
+ Width : 8
+ Poly : 1D
+ Init : FF
+ RefIn : False
+ RefOut : False
+ XorOut : FF
+ Check : 4B
+
+31
+<crc-8-maxim>
+ Name : "CRC-8/MAXIM"
+ Alias : "DOW-CRC"
+ Width : 8
+ Poly : 31
+ Init : 00
+ RefIn : True
+ RefOut : True
+ XorOut : 00
+ Check : A1
+
+9B
+<crc-8-wcdma>
+ Name : "CRC-8/WCDMA"
+ Width : 8
+ Poly : 9B
+ Init : 00
+ RefIn : True
+ RefOut : True
+ XorOut : 00
+ Check : 25
+
+8D
+<crc-8-ccitt>
+ Name : "CRC-8/CCITT" (new entry) 1-Wire?
+ Width : 8
+ Poly : 8D
+ Init : 00?
+ RefIn : False?
+ RefOut : False?
+ XorOut : 00?
+ Check : D2
+
+D5
+<crc-8>
+ Name : "CRC-8" (new entry)
+ Width : 8
+ Poly : D5
+ Init : 00?
+ RefIn : False?
+ RefOut : False?
+ XorOut : 00?
+ Check : BC
+
+CRC-16
+8005
+<crc-16>
+ Name : "ARC"
+ Alias : "CRC-16"
+ Alias : "CRC-IBM"
+ Alias : "CRC-16/ARC"
+ Alias : "CRC-16/LHA"
+ Width : 16
+ Poly : 8005
+ Init : 0000
+ RefIn : True
+ RefOut : True
+ XorOut : 0000
+ Check : BB3D
+
+<crc-16-buypass>
+ Name : "CRC-16/BUYPASS"
+ Alias : "CRC-16/VERIFONE"
+ Width : 16
+ Poly : 8005
+ Init : 0000
+ RefIn : False
+ RefOut : False
+ XorOut : 0000
+ Check : FEE8
+
+<crc-dds-110>
+ Name : "CRC-16/DDS-110"
+ Width : 16
+ Poly : 8005
+ Init : 800D
+ RefIn : False
+ RefOut : False
+ XorOut : 0000
+ Check : 9ECF
+ XCheck : CFE9
+
+<crc-16-maxim>
+ Name : "CRC-16/MAXIM"
+ Width : 16
+ Poly : 8005
+ Init : 0000
+ RefIn : True
+ RefOut : True
+ XorOut : FFFF
+ Check : 44C2
+
+<crc-usb>
+ Name : "CRC-16/USB"
+ Width : 16
+ Poly : 8005
+ Init : FFFF
+ RefIn : True
+ RefOut : True
+ XorOut : FFFF
+ Check : B4C8
+
+<crc-modbus>
+ Name : "MODBUS"
+ Width : 16
+ Poly : 8005
+ Init : FFFF
+ RefIn : True
+ RefOut : True
+ XorOut : 0000
+ Check : 4B37
+
+1021
+<crc-ccitt-1d0f>
+ Name : "CRC-16/AUG-CCITT"
+ Alias : "CRC-16/SPI-FUJITSU"
+ Width : 16
+ Poly : 1021
+ Init : 1D0F
+ RefIn : False
+ RefOut : False
+ XorOut : 0000
+ Check : E5CC
+
+<crc-ccitt-ffff>
+ Name : "CRC-16/CCITT-FALSE"
+ Width : 16
+ Poly : 1021
+ Init : FFFF
+ RefIn : False
+ RefOut : False
+ XorOut : 0000
+ Check : 29B1
+
+<crc-genibus>
+ Name : "CRC-16/GENIBUS"
+ Alias : "CRC-16/I-CODE"
+ Alias : "CRC-16/DARC"
+ Width : 16
+ Poly : 1021
+ Init : FFFF
+ RefIn : False
+ RefOut : False
+ XorOut : FFFF
+ Check : D64E
+
+<crc-ccitt-xmodem>
+ Name : "XMODEM"
+ Alias : "ZMODEM"
+ Alias : "CRC-16/ACORN"
+ Width : 16
+ Poly : 1021
+ Init : 0000
+ RefIn : False
+ RefOut : False
+ XorOut : 0000
+ Check : 31C3
+
+<crc-mcrf4xx>
+ Name : "CRC-16/MCRF4XX"
+ Width : 16
+ Poly : 1021
+ Init : FFFF
+ RefIn : True
+ RefOut : True
+ XorOut : 0000
+ Check : 6F91
+
+<crc-riello>
+ Name : "CRC-16/RIELLO"
+ Width : 16
+ Poly : 1021
+ Init : B2AA
+ RefIn : True
+ RefOut : True
+ XorOut : 0000
+ Check : 63D0
+
+ <crc-ccitt-kermit>
+ Name : "KERMIT"
+ Alias : "CRC-16/CCITT"
+ Alias : "CRC-16/CCITT-TRUE"
+ Alias : "CRC-CCITT"
+ Width : 16
+ Poly : 1021
+ Init : 0000
+ RefIn : True
+ RefOut : True
+ XorOut : 0000
+ Check : 2189
+ XCheck : 8921
+
+<crc-x25>
+ Name : "X-25"
+ Alias : "CRC-16/IBM-SDLC"
+ Alias : "CRC-16/ISO-HDLC"
+ Width : 16
+ Poly : 1021
+ Init : FFFF
+ RefIn : True
+ RefOut : True
+ XorOut : FFFF
+ Check : 906E
+ XCheck : 6E90
+
+0589
+<crc-dect-r>
+ Name : "CRC-16/DECT-R"
+ Alias : "R-CRC-16"
+ Width : 16
+ Poly : 0589
+ Init : 0000
+ RefIn : False
+ RefOut : False
+ XorOut : 0001
+ Check : 007E
+
+<crc-dect-x>
+ Name : "CRC-16/DECT-X"
+ Alias : "X-CRC-16"
+ Width : 16
+ Poly : 0589
+ Init : 0000
+ RefIn : False
+ RefOut : False
+ XorOut : 0000
+ Check : 007F
+
+3D65
+<crc-dnp>
+ Name : "CRC-16/DNP"
+ Width : 16
+ Poly : 3D65
+ Init : 0000
+ RefIn : True
+ RefOut : True
+ XorOut : FFFF
+ Check : EA82
+ XCheck : 82EA
+
+<crc-en-13757>
+ Name : "CRC-16/EN-13757"
+ Width : 16
+ Poly : 3D65
+ Init : 0000
+ RefIn : False
+ RefOut : False
+ XorOut : FFFF
+ Check : C2B7
+
+8BB7
+<crc-t10-dif>
+ Name : "CRC-16/T10-DIF"
+ Width : 16
+ Poly : 8BB7
+ Init : 0000
+ RefIn : False
+ RefOut : False
+ XorOut : 0000
+ Check : D0DB
+
+A097
+<crc-teledisk>
+ Name : "CRC-16/TELEDISK"
+ Width : 16
+ Poly : A097
+ Init : 0000
+ RefIn : False
+ RefOut : False
+ XorOut : 0000
+ Check : 0FB3
+
+CRC-24
+864CFB
+<crc-24>
+ Name : "CRC-24"
+ Alias : "CRC-24/OPENPGP"
+ Width : 24
+ Poly : 864CFB
+ Init : B704CE
+ RefIn : False
+ RefOut : False
+ XorOut : 000000
+ Check : 21CF02
+
+5D6DCB
+<crc-24-flexray-a>
+ Name : "CRC-24/FLEXRAY-A"
+ Width : 24
+ Poly : 5D6DCB
+ Init : FEDCBA
+ RefIn : False
+ RefOut : False
+ XorOut : 000000
+ Check : 7979BD
+
+<crc-24-flexray-b>
+ Name : "CRC-24/FLEXRAY-B"
+ Width : 24
+ Poly : 5D6DCB
+ Init : ABCDEF
+ RefIn : False
+ RefOut : False
+ XorOut : 000000
+ Check : 1F23B8
+
+CRC-32
+04C11DB7
+<crc-32>
+ Name : "CRC-32"
+ Alias : "CRC-32/ADCCP"
+ Alias : "PKZIP"
+ Width : 32
+ Poly : 04C11DB7
+ Init : FFFFFFFF
+ RefIn : True
+ RefOut : True
+ XorOut : FFFFFFFF
+ Check : CBF43926
+
+<crc-32-bzip2>
+ Name : "CRC-32/BZIP2"
+ Alias : "B-CRC-32"
+ Width : 32
+ Poly : 04C11DB7
+ Init : FFFFFFFF
+ RefIn : False
+ RefOut : False
+ XorOut : FFFFFFFF
+ Check : FC891918
+
+<crc-32-mpeg-2>
+ Name : "CRC-32/MPEG-2"
+ Width : 32
+ Poly : 04C11DB7
+ Init : FFFFFFFF
+ RefIn : False
+ RefOut : False
+ XorOut : 00000000
+ Check : 0376E6E7
+
+<crc-32-posix>
+ Name : "CRC-32/POSIX"
+ Alias : "CKSUM"
+ Width : 32
+ Poly : 04C11DB7
+ Init : 00000000
+ RefIn : False
+ RefOut : False
+ XorOut : FFFFFFFF
+ Check : 765E7680
+ LCheck : 377A6011
+
+<crc-32-jamcrc>
+ Name : "JAMCRC"
+ Width : 32
+ Poly : 04C11DB7
+ Init : FFFFFFFF
+ RefIn : True
+ RefOut : True
+ XorOut : 00000000
+ Check : 340BC6D9
+
+1EDC6F41
+<crc-32-C>
+ Name : "CRC-32C"
+ Alias : "CRC-32/ISCSI"
+ Alias : "CRC-32/CASTAGNOLI"
+ Width : 32
+ Poly : 1EDC6F41
+ Init : FFFFFFFF
+ RefIn : True
+ RefOut : True
+ XorOut : FFFFFFFF
+ Check : E3069283
+
+A833982B
+<crc-32-D>
+ Name : "CRC-32D"
+ Width : 32
+ Poly : A833982B
+ Init : FFFFFFFF
+ RefIn : True
+ RefOut : True
+ XorOut : FFFFFFFF
+ Check : 87315576
+
+741B8CD7
+<crc-32-K> (new entry)
+ Name : "CRC-32K"
+ Alias : "CRC-32/KOOPMAN"
+ Width : 32
+ Poly : 741B8CD7
+ Init : 00000000?
+ RefIn : False?
+ RefOut : False?
+ XorOut : 00000000?
+ Check : 085A3197 ?
+
+814141AB
+<crc-32-Q>
+ Name : "CRC-32Q"
+ Width : 32
+ Poly : 814141AB
+ Init : 00000000
+ RefIn : False
+ RefOut : False
+ XorOut : 00000000
+ Check : 3010BF7F
+
+000000AF
+<crc-32-xfer>
+ Name : "XFER"
+ Width : 32
+ Poly : 000000AF
+ Init : 00000000
+ RefIn : False
+ RefOut : False
+ XorOut : 00000000
+ Check : BD0BE338
+
+CRC-40
+<crc-40-gsm>
+ Name : "CRC-40/GSM"
+ Width : 40
+ Poly : 0004820009
+ Init : 0000000000
+ RefIn : False
+ RefOut : False
+ XorOut : 0000000000
+ Check : 2BE9B039B9
+
+CRC-64
+42F0E1EBA9EA3693
+<crc-64>
+ Name : "CRC-64"
+ Width : 64
+ Poly : 42F0E1EBA9EA3693
+ Init : 0000000000000000
+ RefIn : False
+ RefOut : False
+ XorOut : 0000000000000000
+ Check : 6C40DF5F0B497347
+
+<crc-64-we>
+ Name : "CRC-64/WE"
+ Width : 64
+ Poly : 42F0E1EBA9EA3693
+ Init : FFFFFFFFFFFFFFFF
+ RefIn : False
+ RefOut : False
+ XorOut : FFFFFFFFFFFFFFFF
+ Check : 62EC59E3F1A4F00A
+
+000000000000001B
+<crc-64-1b>
+ Name : "CRC-64/1B" (New entry)
+ Width : 64
+ Poly : 000000000000001B
+ Init : 0000000000000000
+ RefIn : True
+ RefOut : True
+ XorOut : 0000000000000000
+ Check : 46A5A9388A5BEFFE
+
+AD93D23594C935A9
+<crc-64-jones>
+ Name : "CRC-64/Jones" (New entry)
+ Width : 64
+ Poly : AD93D23594C935A9
+ Init : FFFFFFFFFFFFFFFF
+ RefIn : True
+ RefOut : True
+ XorOut : 0000000000000000
+ Check : CAA717168609F281
diff --git a/tools/hex2bin-2.0/doc/ChangeLog_hex2bin b/tools/hex2bin-2.0/doc/ChangeLog_hex2bin
new file mode 100644
index 0000000..3339250
--- /dev/null
+++ b/tools/hex2bin-2.0/doc/ChangeLog_hex2bin
@@ -0,0 +1,57 @@
+(UTF8 encoding)
+
+- hex2bin 1.0.12 - 20141122 Simone Fratini
+ small feature added
+ 20141121 Slucx
+ added line for removing extra CR when entering file name at run time.
+ 20141008 JP
+ removed junk code
+
+- hex2bin 1.0.11 - 20141005 Jacques Pelletier
+ added option to support byte-swapped hex used by Microchip's MPLAB IDE
+ corrected bug caused by extra LF at end or within file
+
+- hex2bin 1.0.10 - 20120509 Yoshimasa Nakane
+ modified error checking (also for output file, JP)
+ modified option parser (JP)
+
+- hex2bin 1.0.9 - 20120125 - Danny Schneider
+ Added code for filling a binary file to a given Max_Length relative to
+ Starting Address if Max-Address is larger than Highest-Address
+
+- hex2bin 1.0.8 - 20100402 - Jacques Pelletier
+ Fixed a bug with physical address calculation with extended linear address records
+ ADDRESS_MASK is now calculated from MEMORY_SIZE
+
+- hex2bin 1.0.7 - 20091212 - Jacques Pelletier
+ Fixed the crash on 0 byte length data records
+
+- hex2bin 1.0.6 - 20080103 - Jacques Pelletier
+ Fixed a bug when generating binary files near the end of the buffer
+
+- hex2bin 1.0.5 - 20071005 - Paweł Grabski -
+ Improved parsing of options.
+
+- hex2bin 1.0.4 - 20050126 - Jacques Pelletier -
+ Corrected the conversion LF -> CR+LF bug
+ applied patch for correcting the incorrect handling of
+ extended segment address record
+ added the Rockwell checksum extensions, and modified them a bit to allow
+ other types later.
+
+- hex2bin 1.0.3 - 20040617 - Alf Lacis -
+ Added pad byte (may not always want FF).
+ Added 'break;' to remove GNU compiler warning about label at
+ end of compound statement
+ Added PROGRAM & VERSION strings.
+
+- hex2bin 1.0.2 -
+ Corrected Bug in checksum verification
+
+- hex2bin 1.0.1 -
+ Added checking for memory indexing out of bound.
+ Added segmented and linear extended addressing in hex2bin.
+ Corrected an error: & were interverted with && (and bitwise, logical and).
+
+- hex2bin 1.0.0 -
+ Initial release
diff --git a/tools/hex2bin-2.0/doc/ChangeLog_mot2bin b/tools/hex2bin-2.0/doc/ChangeLog_mot2bin
new file mode 100644
index 0000000..4e3c7b3
--- /dev/null
+++ b/tools/hex2bin-2.0/doc/ChangeLog_mot2bin
@@ -0,0 +1,49 @@
+(UTF8 encoding tab = 4)
+- mot2bin 1.0.12 - 20141122 Simone Fratini
+ small feature added
+ 20141121 Slucx
+ added line for removing extra CR when entering file name at run time.
+
+- mot2bin 1.0.11 - 20141005 Jacques Pelletier
+ added option to support byte-swapped hex used by Microchip's MPLAB IDE
+ corrected bug caused by extra LF at end or within file
+
+- mot2bin 1.0.10 - 20120509 Yoshimasa Nakane
+ modified error checking (also for output file, JP)
+
+- mot2bin 1.0.9 - 20120125 - Danny Schneider
+ Added code for filling a binary file to a given Max_Length relative to
+ Starting Address if Max-Address is larger than Highest-Address
+ (JP) corrected a bug in the checksum checking
+ (JP) added code for record types 0,5,7,8,9
+
+- mot2bin 1.0.8 - 20100402 - Jacques Pelletier
+ ADDRESS_MASK is now calculated from MEMORY_SIZE
+
+- mot2bin 1.0.7 - 20091212 - Jacques Pelletier
+ Fixed the crash on 0 byte length data records
+
+- mot2bin 1.0.6 - 20080103 - Jacques Pelletier
+ Corrected a bug when generating a binary file near the end of the buffer.
+
+- mot2bin 1.0.5 - 20071005 - Paweł Grabski -
+ Improved parsing of options (same code as hex2bin).
+
+- mot2bin 1.0.4 - 20050128 - Jacques Pelletier -
+ Modified the checksum code to be able to generate other checksum types
+ later (ex. CRC).
+
+- mot2bin 1.0.3 - 20041026 - Scott A. Mintz -
+ Modified the MOT2BIN file to compute a checksum over a range using
+ 8bit, 16bit little endian, or 16bit big endian and optionally forcing
+ the checksum to a specific value by modifying a memory location.
+
+- mot2bin 1.0.2 - 20040617 - Alf Lacis -
+ Added pad byte (may not always want FF).
+ Added initialisation to Checksum to remove GNU
+ compiler warning about possible uninitialised usage
+ Added 2x'break;' to remove GNU compiler warning about label at
+ end of compound statement
+ Added PROGRAM & VERSION strings.
+
+- no previous ChangeLog -
diff --git a/tools/hex2bin-2.0/doc/README b/tools/hex2bin-2.0/doc/README
new file mode 100644
index 0000000..78ee7e7
--- /dev/null
+++ b/tools/hex2bin-2.0/doc/README
@@ -0,0 +1,225 @@
+Yet Another Hex to bin converter
+
+It can handle the extended Intel hex format in segmented and linear address
+modes. Records need not be sorted and there can be gaps between records.
+
+Some hex files are produced by compilers. They generate objects files for each
+module in a project, and when the linker generates the final hex file, the
+object files are stored within the hex files, but modules can appear not
+necessary in order of address.
+
+How does it work?
+
+Hex2bin/mot2bin allocates a 4 MBytes buffer and just place the converted bytes
+in its buffer. At the end, the buffer is written to disk. Using a buffer elimi-
+nates the need to sort records. If the option l is used (3), the buffer will be
+allocated with the maximum size specified if over 4Mbytes.
+
+Before reading the hex file, the buffer is filled with a default value. These
+padding bytes are all FF by default so an EPROM programmer can skip these bytes
+when programming. The padding value can be changed with the -p option.
+
+1. Compiling on Linux or other unix platforms
+
+ make
+
+ then
+
+ make install
+
+ This will install the program to /usr/local/bin.
+
+1a. Compiling for Windows on Msys, Cygwin or DOS prompt
+
+ The programs can be compiled as follows:
+ gcc -O2 -Wall -o hex2bin.exe hex2bin.c common.c libcrc.c binary.c
+ gcc -O2 -Wall -o mot2bin.exe mot2bin.c common.c libcrc.c binary.c
+
+2. Using hex2bin
+
+ hex2bin example.hex
+
+ hex2bin will generate a binary file example.bin starting at the
+ lowest address in the hex file.
+
+3. Binary file starting address and length
+
+ If the lowest address isn't 0000,
+ ex: 0100: (the first record begins with :nn010000xxx )
+
+ there will be problems when using the binary file to program a EPROM
+ since the first byte supposed to be at 0100 is stored in the binary file
+ at 0000.
+
+ you can specify a starting address for the binary file on the command line:
+
+ hex2bin -s 0000 start_at_0100.hex
+
+ This start address is not the same thing as the start address record in
+ the hex file. The start address record is used to specify the starting
+ address for execution of the binary code.
+
+ The bytes will be stored in the binary file with a padding from 0000
+ to the lowest address minus 1 (00FF in this case).
+
+ Similarly, the binary file can be padded up to Length -1 with FF or another byte.
+
+ Here, the space between the last byte and 07FF will be filled with FF.
+ hex2bin -l 0800 ends_before_07FF.hex
+
+ EPROM, EEPROM and Flash memories contain all FF when erased.
+
+ This program does minimal error checking since many hex files are
+ generated by known good assemblers.
+
+ When the source file name is
+ for-example.test.hex
+ the binary created will have the name
+ for-example.bin
+ the ".test" part will be dropped.
+
+ Hex2bin/mot2bin assume the source file doesn't contain overlapping records,
+ if so, overlaps will be reported.
+
+4. Checksum of source file
+
+ By default, it ignores record checksum errors, so that someone can change
+ by hand some bytes allowing quick and dirty changes.
+ If you want checksum error reporting, specify the option -c.
+
+ hex2bin -c example.hex
+
+ If there is a record checksum error somewhere, the program will continue the
+ conversion anyway.
+
+ The example file example.hex contains some records with checksum errors.
+
+5. Check value inserted inside binary file
+
+ A check value can be inserted in the resulting binary file.
+
+ hex2bin -k [0-4] -r [start] [end] -f [address] -C [Poly] [Init] [RefIn] [RefOut] [XorOut]
+
+ -k Select the check method:
+ 0: Checksum 8-bit
+ 1: Checksum 16-bit
+ 2: CRC8
+ 3: CRC16
+ 4: CRC32
+
+ -r Range to compute checksum over (default is min and max addresses)
+
+ -f Address of the result to write
+
+ -C Parameters for CRC
+ Parameters for common CRCs are listed in doc/CRC list.txt. They appear in
+ the same order. Feed them as is and use t for TRUE, f for FALSE.
+
+ See also the test/Makefile for these common CRCs; since they're tested,
+ you'll have the command line figured out.
+
+ -E Endian for storing the check result or forcing it
+ 0: little
+ 1: big
+
+ Change from previous versions of hex2bin/mot2bin:
+ Replace former options to this version
+ -k 1 -> -k 1 -E 0
+ -k 2 -> -k 1 -E 1
+
+6. Value inserted directly inside binary file
+ Instead of calculating a value, it can be inserted directly into the file at a specified address.
+
+ hex2bin -k [0|1|2] -F [address] [value]
+
+ -k Select the value format:
+
+ 0 = 8-bit
+ 1 = 16-bit
+ 2 = 32-bit
+
+ -F Address and value of checksum to force
+
+ -E Endian for storing the check result or forcing it
+ 0: little
+ 1: big
+
+7. Motorola S files
+
+ mot2bin example.s19
+
+ Options for mot2bin are the same as hex2bin. Executing the program
+ without argument will display available options. Some are specific to
+ Motorola files.
+
+ This program will handle S19 files generated for Motorola micropro-
+ cessors. Since I use this program for an EPROM programmer, I will
+ rarely need to have more than 4M, I limited the source program for
+ 24 bits or 16 bits address records.
+
+ 32 bits records are now supported, but obviously I can't allocate all
+ the memory for the binary target. What I did is simply assume that the
+ binary file will occupy less than 4M. For binary files greater than 4M,
+ see length option (section 3).
+
+8. Support for byte-swapped hex/S19 files
+
+ -w Wordwise swap: for each pair of bytes, exchange the low and high part.
+ If a checksum needs to be generated to insert in the binary file, select
+ one of the 16-bit checksums.
+
+ hex2bin -w test-byte-swap.hex
+
+9. Goodies
+
+ Description of the file formats is included.
+ Added examples files for extended addressing.
+
+ Check for overlapping records. The check is rather basic: supposing
+ that the buffer is filled with pad bytes, when a record overlaps a
+ previous one, value in the buffer will be different from the pad bytes.
+ This will not detect the case when the previous value equals the pad byte,
+ but it's more likely that more than one byte will be overlapped.
+
+
+10. Error messages
+
+ "Can't allocate memory."
+
+ Can't do anything in this case, so the program simply exits.
+
+ "Error occurred while reading from file"
+
+ Problem with fgets.
+
+ "Input/Output file %s cannot be opened. Enter new filename: "
+
+ The user may not have permissions to open the file.
+
+ "0 byte length data record ignored"
+
+ This means that an empty data record was read. Since it's empty, it's simply
+ ignored and should have no impact on the binary file.
+
+ "Data record skipped at ..."
+
+ This means that the records are falling outside the memory buffer.
+
+ "Overlapped record detected"
+
+ A record is overwritten by a subsequent record. If you're using SDCC, check
+ if more than one area is specified with a starting address. Checking the map
+ file generated by the linker can help.
+
+ "Some error occurred when parsing options."
+
+
+
+11. History
+
+ See ChangeLog
+
+12. Other hex tool
+
+ There is a program that supports more formats and has more features.
+ See SRecord at http://srecord.sourceforge.net/
diff --git a/tools/hex2bin-2.0/doc/S-record.txt b/tools/hex2bin-2.0/doc/S-record.txt
new file mode 100644
index 0000000..ba4abc1
--- /dev/null
+++ b/tools/hex2bin-2.0/doc/S-record.txt
@@ -0,0 +1,361 @@
+S-Record Format
+
+ A file in Motorola S-record format is an ASCII file. There are three different
+ formats:
+
+ S19 for 16-bit address
+ S2 for 24-bit address
+ S3 for 32-bit address
+
+
+ The files consist of optional symbol table information, data specifications
+ for loading memory, and a terminator record.
+
+ [ $$ {module_record}
+ symbol records
+ $$ [ module_record ]
+ symbol_records
+ $$]
+ header_record
+ data_records
+ record_count_record
+ terminator_record
+
+
+Module Record (Optional)
+
+ Each object file contains one record for each module that is a component of it. This
+ record contains the name of the module. There is one module record for each relocatable
+ object created by the assembler. The name of the relocatable object module
+ contained in the record comes from the IDNT directive. For absolute objects created
+ by the linker, there is one module record for each relocatable object file linked,
+ plus an additional record whose name comes from the NAME command for the
+ linker.
+
+ Example:
+
+ $$ MODNAME
+
+
+Symbol Record (Optional)
+
+ As many symbol records as needed can be contained in the object module. Up to 4
+ symbols per line can be used, but it is not mandatory that each line contain 4
+ symbols. A module can contain only symbol records.
+
+ Example:
+
+ APPLE $00000 LABEL1 $ODOC3
+ MEM $OFFFF ZEEK $01947
+
+ The module name associated with the symbols can be specified in the
+ module_record preceding the symbol records.
+
+ Example:
+
+ $$MAIN
+
+ Symbols are assumed to be in the module named in the preceding module_record
+ until another module is specified with another module_record. Symbols defined by
+ the linker's PUBLIC command appear following the first module record, which
+ indicates the name of the output object module specified by the linker's NAME
+ command.
+
+
+*****************************************************************************************
+
+Header Record (SO)
+
+ Each object module has exactly one header record with the following format:
+
+ S00600004844521B
+
+ Description:
+
+ S0 Identifies the record as a header record
+ 06 The number of bytes following this one
+ 0000 The address field, which is ignored
+ 484452 The string HDR in ASCII
+ 1B The checksum
+
+
+
+*****************************************************************************************
+
+Data Record (S1)
+
+ A data record specifies data bytes that are to be loaded into memory. Figure 1
+ shows the format for such a record. The columns shown in the figure represent half
+ of a byte (4 bits).
+
+ ---------------------------------------------
+ | 1 2 3 4 5 6 7 8 9 ... 40 41 42 |
+ | |
+ | S ID byte load data...data checksum |
+ | count address 1 n |
+ ---------------------------------------------
+ Figure 1: Data Record Formatter 16-Bit Load Address
+
+
+ Column Description
+
+ 1 Contains the ASCII character S, which indicates the start of
+ a record in Motorola S-record format.
+
+ 2 Contains the ASCII character identifying the record type.
+ For data records, this character is 1.
+
+ 3 to 4 Contain the count of the number of bytes following this one
+ within the record. The count includes the checksum and the
+ load address bytes but not the byte count itself.
+
+ 5 to 8 Contain the load address. The first data byte is to be loaded
+ into this address and subsequent bytes into the next sequential
+ address. Columns 5 and 6 contain the high-order address
+ byte, and columns 7 and 8 contain the low-order address byte.
+
+ 9 to 40 Contain the specifications for up to 16 bytes of data.
+
+ 41 to 42 Contain a checksum for the record. To calculate this, take the
+ sum of the values of all bytes from the byte count up to the
+ last data byte, inclusive, modulo 256. Subtract this result
+ from 255.
+
+
+*****************************************************************************************
+
+Data Record (S2)
+
+
+ A data record specifies data bytes that are to be loaded into memory. Figure 2
+ shows the format for such a record. The columns shown in the figure represent half
+ of a byte (4 bits).
+
+
+ ----------------------------------------------------
+ | 1 2 3 4 5 6 7 8 9 10 11 ... 42 43 44 |
+ | |
+ | S ID byte load data...data checksum |
+ | count address 1 n |
+ ----------------------------------------------------
+ Figure 2: Data Record Format for 24-Bit Load Address
+
+ Column Description
+
+ 1 Contains the ASCII character S, which indicates the start of
+ a record in Motorola S-record format.
+
+ 2 Contains the ASCII character identifying the record type.
+ For data records, this character is 2.
+
+ 3 to 4 Contain the count of the number of bytes following this one
+ within the record. The count includes the checksum and the
+ load address bytes but not the byte count itself.
+
+ 5 to 10 Contain the load address. The first data byte is to be loaded
+ into this address and subsequent bytes into the next sequential
+ address. Columns 5 and 6 contain the high-order address
+ byte, and columns 9 and 10 contain the low-order address byte.
+
+ 11 to 42 Contain the specifications for up to 16 bytes of data.
+
+ 43 to 44 Contain a checksum for the record. To calculate this, take the
+ sum of the values of all bytes from the byte count up to the
+ last data byte, inclusive, modulo 256. Subtract this result
+ from 255.
+
+
+*****************************************************************************************
+
+Data Record (S3)
+
+
+ A data record specifies data bytes that are to be loaded into memory. Figure 3
+ shows the format for such a record. The columns shown in the figure represent half
+ of a byte (4 bits).
+
+ ----------------------------------------------------------
+ | 1 2 3 4 5 6 7 8 9 10 11 12 13 ... 44 45 46 |
+ | |
+ | S ID byte load data...data checksum |
+ | count address 1 n |
+ ----------------------------------------------------------
+ Figure 3: Data Record Format for 32-Bit Load Address
+
+ Column Description
+
+ 1 Contains the ASCII character S, which indicates the start of
+ a record in Motorola S-record format.
+
+ 2 Contains the ASCII character identifying the record type.
+ For data records, this digit is 3 for 32-bit addresses.
+
+ 3 to 4 Contain the count of the number of bytes following this one
+ within the record. The count includes the checksum and the
+ load address bytes but not the byte count itself.
+
+ 5 to 12 Contain the load address. The first data byte is to be loaded
+ into this address and subsequent bytes into the next sequential
+ address. Columns 5 and 6 contain the high-order address
+ byte, and columns 11 and 12 contain the low-order address byte.
+
+ 13 to 44 Contain the specifications for up to 15 bytes of data.
+
+ 45 to 46 Contain a checksum for the record. To calculate this, take the
+ sum of the values of all bytes from the byte count up to the
+ last data byte, inclusive, modulo 256. Subtract this result
+ from 255.
+
+
+*****************************************************************************************
+
+Record Count Record (S5)
+
+
+ The record count record verifies the number of data records preceding it. Figure 4
+ shows the format for such a record. The columns shown in the figure represent half
+ of a byte (4 bits).
+
+ --------------------------------------
+ | 1 2 3 4 5 6 7 8 9 10 |
+ | |
+ | S ID byte # of data checksum |
+ | count records |
+ --------------------------------------
+ Figure 4: Record Count Record Format
+
+ Column Description
+
+ 1 Contains the ASCII character S, which indicates the start of
+ a record in Motorola S-record format.
+
+ 2 Contains the ASCII character 5, which indicates a record
+ count record.
+
+ 3 to 4 Contain the byte count, ASCII string 03.
+
+ 5 to 8 Contain the number of data records in this file. The high-
+ order byte is in columns 5 and 6.
+
+ 9 to 10 Contain the checksum for the record.
+
+ Example:
+
+ S503010DEE
+
+ The example above shows a record count record indicating a total of 269 records
+ (0x010D) and a checksum of 0xEE.
+
+
+
+*****************************************************************************************
+
+Terminator Record for 32-bit address (S7)
+
+ A terminator record specifies the end of the data records. Figure 5 shows the
+ format for such a record. The columns shown in the figure represent half of a byte
+ (4 bits).
+
+ -------------------------------------
+ | 1 2 3 4 5...12 13 14 |
+ | |
+ | S ID byte load checksum |
+ | count address |
+ -------------------------------------
+ Figure5: Terminator Record Format for 32-Bit Load Address
+
+ Column Description
+
+ 1 Contains the ASCII character S, which indicates the start of
+ a record in Motorola S-record format.
+
+ 2 Contains the ASCII character 7, which indicates a 32-bit
+ load address.
+
+ 3 to 4 Contain the byte count, ASCII string 04.
+
+ 5 to 12 Contain the load address that is either set to zero or to the
+ starting address specified in the END directive or START
+ command (there are no data bytes).
+
+ 13 to 14 Contain the checksum for the record.
+
+*****************************************************************************************
+
+Terminator Record for 24-bit address (S8)
+
+
+ A terminator record specifies the end of the data records. Figure 6 shows the
+ format for such a record. The columns shown in the figure represent half of a byte
+ (4 bits).
+
+ ----------------------------------------
+ | 1 2 3 4 5 6 7 8 9 10 11 12 |
+ | |
+ | S ID byte load checksum |
+ | count address |
+ ----------------------------------------
+ Figure 6: Terminator Record Format for 24-Bit Load Address
+
+ Column Description
+
+ 1 Contains the ASCII character S, which indicates the start of
+ a record in Motorola S-record format.
+
+ 2 Contains the ASCII character 8, which indicates a 24-bit
+ load address.
+
+ 3 to 4 Contain the byte count, ASCII string 04.
+
+ 5 to 10 Contain the load address, which is either set to zero or to the
+ starting address specified in the END directive or START
+ command. There are no data bytes.
+
+ 11 to 12 Contain the checksum for the record.
+
+ Example:
+
+ S804000AF0001
+
+ The previous example shows a terminator record with a 24-bit load address of
+ 0x000AF0 and a checksum of 0x01.
+
+
+*****************************************************************************************
+
+Terminator Record for 16-bit address (S9)
+
+
+ A terminator record specifies the end of the data records. Figure 7 shows the
+ format for such a record. The columns shown in the figure represent half of a byte
+ (4 bits).
+
+ -------------------------------------
+ | 1 2 3 4 5 6 7 8 9 10 |
+ | |
+ | S ID byte load checksum |
+ | count address |
+ -------------------------------------
+ Figure 7: Terminator Record Format for 16-Bit Load Address
+
+
+ Column Description
+
+ 1 Contains the ASCII character S, which indicates the start of
+ a record in Motorola S-record format.
+
+ 2 Contains the ASCII character 9, which indicates a 16-bit
+ load address.
+
+ 3 to 4 Contain the byte count, ASCII string 04.
+
+ 5 to 8 Contain the load address, which is either set to zero or to the
+ starting address specified in the END directive or START
+ command (there are no data bytes).
+
+ 9 to 10 Contain the checksum for the record.
+
+
+
+*****************************************************************************************
+ hagen.v.tronje@on-line.de
diff --git a/tools/hex2bin-2.0/doc/formats.txt b/tools/hex2bin-2.0/doc/formats.txt
new file mode 100644
index 0000000..25e5e37
--- /dev/null
+++ b/tools/hex2bin-2.0/doc/formats.txt
@@ -0,0 +1,72 @@
+Hex formats
+
+Intel
+=====
+
+Hexadecimal values are always in uppercase. Each line is a record.
+The sum of all the bytes in each record should be 00 (modulo 256).
+
+Record types:
+
+00: data records
+01: end-of-file record
+02: extended address record
+
+Data record
+-----------
+
+ :0D011C0000000000C3E0FF0000000000C30F
+
+: 0D 011C 00 00000000C3E0FF0000000000C3 0F
+| | | | -------------+------------ |
+| | | | | +--- Checksum
+| | | | +------------------ Data bytes
+| | | +--------------------------------- Record type
+| | +------------------------------------- Address
+| +----------------------------------------- Number of data bytes
++-------------------------------------------- Start of record
+
+
+End of file record
+------------------
+
+ :00000001FE
+
+: 00 0000 01 FE
+| | | | |
+| | | | +--- Checksum
+| | | +------ Record type
+| | +---------- Address
+| +-------------- Number of data bytes
++----------------- Start of record
+
+
+
+Extended address record
+-----------------------
+
+ :02010002E0001B
+
+: 02 0100 02 E000 1B
+| | | | | |
+| | | | | +--- Checksum
+| | | | +-------- Segment address
+| | | +----------- Record type
+| | +--------------- Address
+| +------------------- Number of data bytes
++---------------------- Start of record
+
+Following data records will start at E000:0100 or E0100
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/hex2bin-2.0/doc/intelhex.spc b/tools/hex2bin-2.0/doc/intelhex.spc
new file mode 100644
index 0000000..946d586
--- /dev/null
+++ b/tools/hex2bin-2.0/doc/intelhex.spc
@@ -0,0 +1,409 @@
+
+======================================================================
+
+Intel
+Hexadecimal Object File
+Format Specification
+Revision A, 1/6/88
+
+
+
+DISCLAIMER
+
+Intel makes no representation or warranties with respect to the contents
+hereof and specifically disclaims any implied warranties of
+merchantability or fitness for any particular purpose. Further, Intel
+reserves the right to revise this publication from time to time in the
+content hereof without obligation of Intel to notify any person of such
+revision or changes. The publication of this specification should not
+be construed as a commitment on Intel's part to implement any product.
+
+
+1. Introduction
+This document describes the hexadecimal object file format for the Intel
+8- bit, 16-bit, and 32-bit microprocessors. The hexadecimal format is
+suitable as input to PROM programmers or hardware emulators.
+Hexadecimal object file format is a way of representing an absolute
+binary object file in ASCII. Because the file is in ASCII instead of
+binary, it is possible to store the file is non-binary medium such as
+paper-tape, punch cards, etc.; and the file can also be displayed on CRT
+terminals, line printers, etc.. The 8-bit hexadecimal object file
+format allows for the placement of code and data within the 16-bit
+linear address space of the Intel 8-bit processors. The 16-bit
+hexadecimal format allows for the 20-bit segmented address space of the
+Intel 16-bit processors. And the 32-bit format allows for the 32-bit
+linear address space of the Intel 32-bit processors.
+The hexadecimal representation of binary is coded in ASCII alphanumeric
+characters. For example, the 8-bit binary value 0011-1111 is 3F in
+hexadecimal. To code this in ASCII, one 8-bit byte containing the ASCII
+code for the character '3' (0011-0011 or 033H) and one 8-bit byte
+containing the ASCII code for the character 'F' (0100-0110 or 046H) are
+required. For each byte value, the high-order hexadecimal digit is
+always the first digit of the pair of hexadecimal digits. This
+representation (ASCII hexadecimal) requires twice as ma ny bytes as the
+binary representation.
+A hexadecimal object file is blocked into records, each of which
+contains the record type, length, memory load address and checksum in
+addition to the data. There are currently six (6) different types of
+records that are defined, not all combinations of these records are
+meaningful, however. The records are:
+
+Data Record (8-, 16-, or 32-bit formats)
+End of File Record (8-, 16-, or 32-bit formats)
+Extended Segment Address Record (16- or 32-bit formats)
+Start Segment Address Record (16- or 32-bit formats)
+Extended Linear Address Record (32-bit format only)
+Start Linear Address Record (32-bit format only)
+
+
+2. General Record Format
+| RECORD | LOAD | | | INFO | |
+| MARK | RECLEN | OFFSET | RECTYP | or | CHKSUM |
+| ':' | | | | DATA | |
+ 1-byte 1-byte 2-bytes 1-byte n-bytes 1-byte
+
+Each record begins with a RECORD MARK field containing 03AH, the ASCII
+code for the colon (':') character.
+Each record has a RECLEN field which specifies the number of bytes of
+information or data which follows the RECTYP field of the record. Note
+that one data byte is represented by two ASCII characters. The maximum
+value of the RECLEN field is hexadecimal 'FF' or 255.
+Each record has a LOAD OFFSET field which specifies the 16-bit starting
+load offset of the data bytes, therefore this field is only used for
+Data Records. In other records where this field is not used, it should
+be coded as four ASCII zero characters ('0000' or 030303030H).
+Each record has a RECTYP field which specifies the record type of this
+record. The RECTYP field is used to interpret the remaining information
+within the record. The encoding for all the current record types are:
+
+'00' Data Record
+'01' End of File Record
+'02' Extended Segment Address Record
+'03' Start Segment Address Record
+'04' Extended Linear Address Record
+'05' Start Linear Address Record
+
+Each record has a variable length INFO/DATA field, it consists of zero
+or more bytes encoded as pairs of hexadecimal digits. The
+interpretation of this field depends on the RECTYP field.
+Each record ends with a CHKSUM field that contains the ASCII hexadecimal
+representation of the two's complement of the 8-bit bytes that result
+from converting each pair of ASCII hexadecimal digits to one byte of
+binary, from and including the RECLEN field to and including the last
+byte of the INFO/DATA field. Therefore, the sum of all the ASCII pairs
+in a record after converting to binary, from the RECLEN field to and
+including the CHKSUM field, is zero.
+
+
+3. Extended Linear Address Record (32-bit format only)
+| RECORD | LOAD | | | | |
+| MARK | RECLEN | OFFSET | RECTYP | ULBA | CHKSUM |
+| ':' | '02' | '0000' | '04' | | |
+ 1-byte 1-byte 2-bytes 1-byte 2-bytes 1-byte
+
+The 32-bit Extended Linear Address Record is used to specify bits 16-31
+of the Linear Base Address (LBA), where bits 0-15 of the LBA are zero.
+Bits 16-31 of the LBA are referred to as the Upper Linear Base Address
+(ULBA). The absolute memory address of a content byte in a subsequent
+Data Record is obtained by adding the LBA to an offset calculated by
+adding the LOAD OFFSET field of the containing Data Record to the index
+of the byte in the Data Record (0, 1, 2, ... n). This offset addition
+is done modulo 4G (i.e., 32-bits), ignoring any carry, so that offset
+wrap-around loading (from OFFFFFFFFH to OOOOOOOOOH) results in wrapping
+around from the end to the beginning of the 4G linear address defined by
+the LBA. The linear address at which a particular byte is loaded is
+calculated as:
+(LBA + DRLO + DRI) MOD 4G
+where:
+DRLO is the LOAD OFFSET field of a Data Record.
+DRI is the data byte index within the Data Record.
+
+When an Extended Linear Address Record defines the value of LBA, it may
+appear anywhere within a 32-bit hexadecimal object file. This value
+remains in effect until another Extended Linear Address Record is
+encountered. The LBA defaults to zero until an Extended Linear Address
+Record is encountered.
+The contents of the individual fields within the record are:
+
+RECORD MARK
+This field contains 03AH, the hexadecimal encoding of the ASCII colon
+(':') character.
+
+RECLEN
+The field contains 03032H, the hexadecimal encoding of the ASCII
+characters '02', which is the length, in bytes, of the ULBA data
+information within this record.
+
+LOAD OFFSET
+This field contains 030303030H, the hexadecimal encoding of the ASCII
+characters '0000', since this field is not used for this record.
+
+RECTYP
+This field contains 03034H, the hexadecimal encoding of the ASCII
+character '04', which specifies the record type to be an Extended Linear
+Address Record.
+
+ULBA
+This field contains four ASCII hexadecimal digits that specify the
+16-bit Upper Linear Base Address value. The high-order byte is the
+10th/llth character pair of the record. The low-order byte is the
+12th/13th character pair of the record.
+
+CHKSUM
+This field contains the check sum on the RECLEN, LOAD OFFSET, RECTYP,
+and ULBA fields.
+
+
+4. Extended Segment Address Record (16- or 32-bit formats)
+| RECORD | LOAD | | | | |
+| MARK | RECLEN | OFFSET | RECTYP | USBA | CHKSUM |
+| ':' | '02' | '0000' | '02' | | |
+ 1-byte 1-byte 2-bytes 1-byte 2-bytes 1-byte
+
+The 16-bit Extended Segment Address Record is used to specify bits 4-19
+of the Segment Base Address (SBA), where bits 0-3 of the SBA are zero.
+Bits 4-19 of the SBA are referred to as the Upper Segment Base Address
+(USBA). The absolute memory address of a content byte in a subsequent
+Data Record is obtained by adding the SBA to an offset calculated by
+adding the LOAD OFFSET field of the containing Data Record to the index
+of the byte in the Data Record (0, 1, 2, ... n). This offset addition
+is done modulo 64K (i.e., 16-bits), ignoring any carry, so that offset
+wraparound loading (from OFFFFH to OOOOOH) results in wrapping around
+from the end to the beginning of the 64K segment defined by the SBA.
+The address at which a particular byte is loaded is calculated as:
+
+ SBA + ([DRLO + DRI] MOD 64K)
+
+where:
+ DRLO is the LOAD OFFSET field of a Data Record.
+ DRI is the data byte index within the Data Record.
+
+When an Extended Segment Address Record defines the value of SBA, it
+may appear anywhere within a 16-bit hexadecimal object file. This
+value remains in effect until another Extended Segment Address
+Record is encountered. The SBA defaults to zero until an Extended
+Segment Address Record is encountered.
+
+The contents of the individual fields within the record are:
+
+RECORD MARK
+This field contains 03AH, the hexadecimal encoding of the ASCII
+colon (':') character.
+
+RECLEN
+The field contains 03032H, the hexadecimal encoding of the ASCII
+characters '02', which is the length, in bytes, of the USBA data
+information within this record.
+
+LOAD OFFSET
+This field contains 030303030H, the hexadecimal encoding of the
+ASCII characters '0000', since this field is not used for this
+record.
+
+RECTYP
+This field contains 03032H, the hexadecimal encoding of the ASCII
+character '02', which specifies the record type to be an Extended
+Segment Address Record.
+
+USBA
+This field contains four ASCII hexadecimal digits that specify the
+16-bit Upper Segment Base Address value. The high-order byte is the
+10th/1lth character pair of the record. The low-order byte is the
+12th/13th character pair of the record.
+
+CHKSUM
+This field contains the check sum on the RECLEN, LOAD OFFSET,
+RECTYP, and USBA fields.
+
+5. Data Record (8-, 16-, or 32-bit formats)
+
+| RECORD | LOAD | | | | |
+| MARK | RECLEN | OFFSET | RECTYP | DATA | CHKSUM |
+| ':' | | | '00' | | |
+ 1-byte 1-byte 2-bytes 1-byte n-bytes 1-byte
+
+
+The Data Record provides a set of hexadecimal digits that represent
+the ASCII code for data bytes that make up a portion of a memory
+image. The method for calculating the absolute address (linear in
+the 8-bit and 32-bit case and segmented in the 16-bit case) for each
+byte of data is described in the discussions of the Extended Linear
+Address Record and the Extended Segment Address Record.
+
+The contents of the individual fields within the record are:
+
+RECORD MARK
+This field contains 03AH, the hexadecimal encoding of the ASCII
+colon (':') character.
+
+RECLEN
+The field contains two ASCII hexadecimal digits that specify the
+number of data bytes in the record. The maximum value is 'FF' or
+04646H (255 decimal).
+
+LOAD OFFSET
+This field contains four ASCII hexadecimal digits representing the
+offset from the LBA (see Extended Linear Address Record) or SBA (see
+Extended Segment Address Record) defining the address which the
+first byte of the data is to be placed.
+
+RECTYP
+This field contains 03030H, the hexadecimal encoding of the ASCII
+character '00', which specifies the record type to be a Data Record.
+
+DATA
+This field contains pairs of ASCII hexadecimal digits, one pair for
+each data byte.
+
+CHKSUM
+This field contains the check sum on the RECLEN, LOAD OFFSET,
+RECTYP, and DATA fields.
+
+
+6. Start Linear Address Record (32-bit format only)
+
+| RECORD | LOAD | | | | |
+| MARK | RECLEN | OFFSET | RECTYP | EIP | CHKSUM |
+| ':' | '04' | '0000' | '05' | | |
+ 1-byte 1-byte 2-bytes 1-byte 4-bytes 1-byte
+
+
+The Start Linear Address Record is used to specify the execution
+start address for the object file. The value given is the 32-bit
+linear address for the EIP register. Note that this record only
+specifies the code address within the 32-bit linear address space of
+the 80386. If the code is to start execution in the real mode of
+the 80386, then the Start Segment Address Record should be used
+instead, since that record specifies both the CS and IP register
+contents necessary for real mode.
+
+The Start Linear Address Record can appear anywhere in a 32-bit
+hexadecimal object file. If such a record is not present in a
+hexadecimal object file, a loader is free to assign a default start
+address.
+
+The contents of the individual fields within the record are:
+
+RECORD MARK
+This field contains 03AH, the hexadecimal encoding of the ASCII
+colon (':') character.
+
+RECLEN
+The field contains 03034H, the hexadecimal encoding of the ASCII
+characters '04', which is the length, in bytes, of the EIP register
+content within this record.
+
+LOAD OFFSET
+This field contains 030303030H, the hexadecimal encoding of the
+ASCII characters '0000', since this field is not used for this
+record.
+
+RECTYP
+This field contains 03035H, the hexadecimal encoding of the ASCII
+character '05', which specifies the record type to be a Start Linear
+Address Record.
+
+EIP
+This field contains eight ASCII hexadecimal digits that specify the
+32-bit EIP register contents. The high-order byte is the 10th/1lth
+character pair.
+
+CHKSUM
+This field contains the check sum on the RECLEN, LOAD OFFSET,
+RECTYP, and EIP fields.
+
+
+7. Start Segment Address Record (16- or 32-bit formats)
+
+| RECORD | LOAD | | | | |
+| MARK | RECLEN | OFFSET | RECTYP | CS/IP | CHKSUM |
+| ':' | '04' | '0000' | '03' | | |
+ 1-byte 1-byte 2-bytes 1-byte 4-bytes 1-byte
+
+
+The Start Segment Address Record is used to specify the execution
+start address for the object file. The value given is the 20-bit
+segment address for the CS and IP registers. Note that this record
+only specifies the code address within the 20-bit segmented address
+space of the 8086/80186.
+
+The Start Segment Address Record can appear anywhere in a 16-bit
+hexadecimal object file. If such a record is not present in a
+hexadecimal object file, a loader is free to assign a default start
+address.
+
+The contents of the individual fields within the record are:
+
+RECORD MARK
+This field contains 03AH, the hexadecimal encoding of the ASCII
+colon (':') character.
+
+RECLEN
+The field contains 03034H, the hexadecimal encoding of the ASCII
+characters '04', which is the length, in bytes, of the CS/IP
+register contents within this record.
+
+LOAD OFFSET
+This field contains 030303030H, the hexadecimal encoding of the
+ASCII characters '0000', since this field is not used for this
+record.
+
+RECTYP
+This field contains 03033H, the hexadecimal encoding of the ASCII
+character '03', which specifies the record type to be a Start
+Segment Address Record.
+
+CS/IP
+This field contains eight ASCII hexadecimal digits that specify the
+16-bit CS register and 16-bit IP register contents. The high-order
+byte of the CS register content is the 10th/llth character pair, the
+low-order byte is the 12th/13th character pair of the record. The
+high-order byte of the IP register content is the 14th/15th
+character pair, the low-order byte is the 16th/17th character pair
+of the record.
+
+CHKSUM
+This field contains the check sum on the RECLEN, LOAD OFFSET,
+RECTYP, and CS/IP fields.
+
+
+
+8. End of File Record (8-, 16-, or 32-bit formats)
+
+| RECORD | LOAD | | | |
+| MARK | RECLEN | OFFSET | RECTYP | CHKSUM |
+| ':' | '00' | '0000' | '01' | |
+ 1-byte 1-byte 2-bytes 1-byte 1-byte
+
+The End of File Record specifies the end of the hexadecimal object
+file.
+
+The contents of the individual fields within the record are:
+
+RECORD MARK
+This field contains 03AH, the hexadecimal encoding of the ASCII
+colon (':') character.
+
+RECLEN
+The field contains 03030H, the hexadecimal encoding of the ASCII
+characters '00'. Since this record does not contain any INFO/DATA
+bytes, the length is zero.
+
+LOAD OFFSET
+This field contains 030303030H, the hexadecimal encoding of the
+ASCII characters '0000', since this field is not used for this
+record.
+
+RECTYP
+This field contains 03031H, the hexadecimal encoding of the ASCII
+character '01', which specifies the record type to be an End of File
+Record.
+
+CHKSUM
+This field contains the check sum an the RECLEN, LOAD OFFSET, and
+RECTYP fields. Since all the fields are static, the check sum can
+also be calculated statically, and the value is 04646H, the
+hexadecimal encoding of the ASCII characters 'FF'.
+
+========================================================================
+END
diff --git a/tools/hex2bin-2.0/doc/srec.txt b/tools/hex2bin-2.0/doc/srec.txt
new file mode 100644
index 0000000..ea63d31
--- /dev/null
+++ b/tools/hex2bin-2.0/doc/srec.txt
@@ -0,0 +1,447 @@
+S-Records
+
+
+ -S-Record Format-
+
+ Chaplin@keinstr.uucp (Roger Chaplin) reposted an article written
+ by mcdchg!motmpl!ron (Ron Widell) that explained how Motorola
+ S-Records are formatted. This comes from a unix man page. No
+ mention of which version of Unix is specified. This section
+ of the FAQ is a bit long. An anonymous ftp archive is currently
+ being sought. When one is found, the section will be placed in
+ the archive.
+
+
+ SREC(4) UNIX 5.0 (03/21/84) SREC(4)
+
+
+ An S-record file consists of a sequence of specially formatted
+ ASCII character strings. An S-record will be less than or equal to
+ 78 bytes in length.
+
+ The order of S-records within a file is of no significance and no
+ particular order may be assumed.
+
+ The general format of an S-record follow:
+
+ +------------------//-------------------//-----------------------+
+ | type | count | address | data | checksum |
+ +------------------//-------------------//-----------------------+
+
+ type A char-2- field. These characters describe the
+ type of record (S0, S1, S2, S3, S5, S7, S8, or
+ S9).
+ count A char-2- field. These characters when paired and
+ interpreted as a hexadecimal value, display the
+ count of remaining character pairs in the record.
+
+ address A char-4,6, or 8- field. These characters grouped
+ and interpreted as a hexadecimal value, display
+ the address at which the data field is to be
+ loaded into memory. The length of the field
+ depends on the number of bytes necessary to hold
+ the address. A 2-byte address uses 4 characters,
+ a 3-byte address uses 6 characters, and a 4-byte
+ address uses 8 characters.
+ data A char -0-64- field. These characters when paired
+ and interpreted as hexadecimal values represent
+ the memory loadable data or descriptive
+ information.
+
+ checksum A char-2- field. These characters when paired and
+ interpreted as a hexadecimal value display the
+ least significant byte of the ones complement of
+ the sum of the byte values represented by the
+ pairs of characters making up the count, the
+ address, and the data fields.
+
+ Each record is terminated with a line feed. If any
+ additional or different record terminator(s) or delay
+ characters are needed during transmission to the target
+ system it is the responsibility of the transmitting program
+ to provide them.
+
+ S0 Record The type of record is 'S0' (0x5330). The address
+
+
+ field is unused and will be filled with zeros
+ (0x0000). The header information within the data
+ field is divided into the following subfields.
+
+ mname is char-20- and is the
+ module name.
+ ver is char-2- and is the
+ version number.
+
+ rev is char-2- and is the
+ revision number.
+ description is char-0-36- and is a
+ text comment.
+
+ Each of the subfields is composed of ASCII bytes
+ whose associated characters, when paired,
+ represent one byte hexadecimal values in the case
+ of the version and revision numbers, or represent
+ the hexadecimal values of the ASCII characters
+ comprising the module name and description.
+
+ S1 Record The type of record field is 'S1' (0x5331). The
+ address field is interpreted as a 2-byte address.
+ The data field is composed of memory loadable
+ data.
+ S2 Record The type of record field is 'S2' (0x5332). The
+ address field is interpreted as a 3-byte address.
+ The data field is composed of memory loadable
+ data.
+
+ S3 Record The type of record field is 'S3' (0x5333). The
+ address field is interpreted as a 4-byte address.
+ The data field is composed of memory loadable
+ data.
+ S5 Record The type of record field is 'S5' (0x5335). The
+ address field is interpreted as a 2-byte value
+ and contains the count of S1, S2, and S3 records
+ previously transmitted. There is no data field.
+
+ S7 Record The type of record field is 'S7' (0x5337). The
+ address field contains the starting execution
+ address and is interpreted as 4-byte address.
+ There is no data field.
+ S8 Record The type of record field is 'S8' (0x5338). The
+ address field contains the starting execution
+ address and is interpreted as 3-byte address.
+ There is no data field.
+
+ S9 Record The type of record field is 'S9' (0x5339). The
+ address field contains the starting execution
+ address and is interpreted as 2-byte address.
+ There is no data field.
+
+ EXAMPLE
+
+ Shown below is a typical S-record format file.
+
+ S00600004844521B
+ S1130000285F245F2212226A000424290008237C2A
+ S11300100002000800082629001853812341001813
+ S113002041E900084E42234300182342000824A952
+ S107003000144ED492
+ S5030004F8
+ S9030000FC
+
+ The file consists of one S0 record, four S1 records, one S5
+ record and an S9 record.
+
+ The S0 record is comprised as follows:
+
+ S0 S-record type S0, indicating it is a header
+ record.
+ 06 Hexadecimal 06 (decimal 6), indicating that six
+ character pairs (or ASCII bytes) follow.
+
+ 00 00 Four character 2-byte address field, zeroes in
+ this example.
+ 48 ASCII H, D, and R - "HDR".
+
+ 1B The checksum.
+
+ The first S1 record is comprised as follows:
+ S1 S-record type S1, indicating it is a data record
+ to be loaded at a 2-byte address.
+
+ 13 Hexadecimal 13 (decimal 19), indicating that
+ nineteen character pairs, representing a 2 byte
+ address, 16 bytes of binary data, and a 1 byte
+ checksum, follow.
+ 00 00 Four character 2-byte address field; hexadecimal
+ address 0x0000, where the data which follows is to
+ be loaded.
+
+ 28 5F 24 5F 22 12 22 6A 00 04 24 29 00 08 23 7C Sixteen
+ character pairs representing the actual binary
+ data.
+ 2A The checksum.
+
+ The second and third S1 records each contain 0x13 (19)
+ character pairs and are ended with checksums of 13 and 52,
+ respectively. The fourth S1 record contains 07 character
+ pairs and has a checksum of 92.
+
+ The S5 record is comprised as follows:
+
+ S5 S-record type S5, indicating it is a count record
+ indicating the number of S1 records.
+
+
+
+ 03 Hexadecimal 03 (decimal 3), indicating that three
+ character pairs follow.
+
+ 00 04 Hexadecimal 0004 (decimal 4), indicating that
+ there are four data records previous to this
+ record.
+ F8 The checksum.
+
+ The S9 record is comprised as follows:
+
+ S9 S-record type S9, indicating it is a termination
+ record.
+ 03 Hexadecimal 03 (decimal 3), indicating that three
+ character pairs follow.
+
+ 00 00 The address field, hexadecimal 0 (decimal 0)
+ indicating the starting execution address.
+ FC The checksum.
+
+
+ -Intel Hex ASCII Format-
+
+ Intel HEX-ASCII format takes the form:
+
+ +----------------------------------- Start Character
+ |
+ | +-------------------------------- Byte Count
+ | | (# of data bytes)
+ | |
+ | | +-------------------------- Address of first data.
+ | | |
+ | | | +-------------------- Record Type (00 data,
+ | | | | 01 end of record)
+ | | | |
+ | | | | +------------ Data Bytes
+ | | | | |
+ | | | | | +---- Checksum
+ | | | | | |
+ | / \ / \ / \ / \ / \
+ : B C A A A A T T H H ... H H C C
+
+ An examples:
+
+ :10000000DB00E60F5F1600211100197ED300C3004C
+ :1000100000000101030307070F0F1F1F3F3F7F7FF2
+ :01002000FFE0
+ :00000001FF
+
+ This information comes from _Microprocessors and Programmed
+ Logic_, Second Edition, Kenneth L. Short, 1987, Prentice-Hall,
+ ISBN 0-13-580606-2.
+
+ Provisions have been made for data spaces larger than 64 kBytes.
+ The above reference does not discuss them. I suspect there is
+ a start of segment type record, but I do not know how it is
+ implemented.
+
+----------------------------------------------------------------------------
+
+/* This file contains source code to read a Motorola S-record file into
+** a memory image. The size of the file cannot exceed BUFSIZE of data.
+** The image is then written to disk either as binary data starting at
+** address 0 with no data gaps, or as a C array of unsigned longs.
+** Input lines must be no longer than MAXLINE. No check is made!
+**
+** Author: Eric McRae, Electro-Logic Machines, Inc.
+** Date: Copyright 1994
+**
+** This source code is made available to the public "as is". No
+** warranty is given or implied for it's proper operation. This source
+** code may be used in whole or in part as long as this copyright is
+** included.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Comment the following line for non PC applications */
+#define PCDOS
+
+/* Uncomment the following line if you want a binary output instead of
+** a structure
+*/
+/* #define BINARY */
+
+#ifdef PCDOS /* Intel x86 architecture */
+#define BUFSIZE 49152 /* 48K to avoid segment hopping */
+#else /* Any reasonable (non-segmented) arch... */
+#define BUFSIZE 65536 /* As big as you want */
+#endif
+
+#define MAXLINE 256 /* Length of longest input line + 1 */
+/* Globals */
+FILE *infilePH, *outfilePH; /* Handles for input and output files */
+unsigned char *bufAC, /* Allocated image buffer */
+ *highestPC = NULL; /* Highest buffer address written */
+
+/* Change this string to reflect the name of the output array */
+char headerAC[] = "unsigned long sRec[] =\n{\n";
+
+/* Predeclarations */
+int parsebufN( char * ); /* Does the actual parsing */
+
+void main(int argc, const char * argv[])
+{
+ int c, /* Temp char storage */
+ resN; /* result status */
+ char *lbufPC, lbufAC[MAXLINE];
+ int linectrN = 0; /* Used to correlate parse fail to input line */
+
+#ifndef BINARY
+ int i;
+ unsigned long *codePL;
+ unsigned char *codePC;
+#endif
+
+ /* Check the argument count */
+ if( argc != 3 ) /* If didn't specify input and output files */
+ {
+ printf("Usage: %s: infile outfile\n", argv[0] );
+ exit(1);
+ }
+
+ /* OK, let's open some files */
+ if( ( infilePH = fopen( argv[1], "r" ) )== NULL )
+ {
+ printf("%s: Couldn't open input file %s\n", argv[0], argv[1] );
+ exit(2);
+ }
+
+ if( ( outfilePH = fopen( argv[2], "w" ) ) == NULL )
+ {
+ printf("%s: Couldn't open output file %s\n", argv[0], argv[3] );
+ exit(3);
+ }
+
+ /* OK, get a buffer and clear it. */
+ if( (bufAC = calloc( (size_t)BUFSIZE, (size_t)1 )) == NULL )
+ {
+ printf("%s: Couldn't malloc memory for buffer\n", argv[0] );
+ exit(4);
+ }
+
+ lbufPC = lbufAC; /* Point at beginning of line buffer */
+ while( c = fgetc( infilePH ))
+ {
+ if( (c == '\n') || (c == EOF) ) /* If found end of line or file */
+ { /* Parse the Line */
+ if( ( c == EOF ) && ( ferror( infilePH ) ) )
+ {
+ printf("%s: Error reading input file\n", argv[0] );
+ exit(5);
+ }
+ else
+ { /* OK, have a complete line in buffer */
+ linectrN++; /* Increment line counter */
+ if( lbufPC == lbufAC )
+ break; /* ignore blank lines */
+ *lbufPC = 0; /* Terminate the line string */
+ if( resN = parsebufN( lbufAC ) ) /* Parse data record to mem */
+ {
+ printf("%s: Error reading input file at line %d, return code = %d\n",
+ argv[0], linectrN, resN );
+ exit( resN );
+ }
+ lbufPC = lbufAC; /* Repoint line buffer pointer */
+ } /* End of have a complete line */
+ }
+ else
+ *lbufPC++ = c; /* Place char into line buffer */
+ }
+
+ /* At this point, the input file has been emptied. Now dispose of the
+ ** output data according to compilation mode.
+ */
+
+#ifdef BINARY
+
+ /* Write the buffer back to disk as a binary image */
+ resN = fwrite( bufAC, 1, (size_t)((highestPC - bufAC) + 1), outfilePH );
+ if( resN != (int)( (highestPC - bufAC) + 1) )
+ {
+ printf("%s: Error writing output file\n", argv[0] );
+ exit( 6 );
+ }
+
+#else
+ /* Produce a file that can be included in a C program. Data is read
+ ** from buffer as bytes to avoid portability/endian problems with
+ ** this program.
+ */
+ /* Output header first, then 1 long per line */
+ fwrite( (void *)headerAC, 1, (size_t)(sizeof( headerAC )-1), outfilePH );
+
+ codePL = (unsigned long *)bufAC;
+ for( i = (highestPC - bufAC + 1) / 4; i; i-- ) /* for each long */
+ {
+ codePC = (unsigned char *)codePL++;
+ sprintf(lbufAC, "0x%02x%02x%02x%02x%s",
+ *codePC, *(codePC + 1), *(codePC + 2), *(codePC + 3),
+ i == 1 ? "\n" : ",\n" ); /* No comma after final long */
+ fwrite( lbufAC, 1, (size_t)(strlen( lbufAC )), outfilePH );
+ }
+ /* OK, data has been written out, close end of array */
+ fwrite( "};\n", 1, (size_t)3, outfilePH );
+#endif
+}
+
+/* Function: parsebufV
+** Parses an S-record in the buffer and writes it into the buffer
+** if it is has a valid checksum.
+**
+** Args: pointer to character buffer for null terminated line
+** Returns: int result code: 0 = success, else failure
+*/
+int parsebufN( char *lbufPC )
+{
+ unsigned long addrL;
+ unsigned char cksmB, /* checksum of addr, count, & data length */
+ *bufPC; /* Pointer into memory array */
+ int i, countN, /* Number of bytes represented in record */
+ oheadN, /* Number of overhead (addr + chksum) bytes */
+ tvalN; /* Temp for check checksum */
+
+ switch( *(lbufPC+1) ) /* examine 2nd character on the line */
+ {
+ case '1': /* 16 bit address field */
+ if( sscanf(lbufPC, "S1%2x%4lx", &countN, &addrL ) != 2 )
+ return( 10 ); /* Flag error in S1 record */
+ oheadN = 3; /* 2 address + 1 checksum */
+ break;
+
+ case '2': /* 24 bit address field */
+ if( sscanf(lbufPC, "S2%2x%6lx", &countN, &addrL ) != 2 )
+ return( 11 ); /* Flag error in S2 record */
+ oheadN = 4; /* 3 address + 1 checksum */
+ break;
+
+ case '3': /* 32 bit address field */
+ if( sscanf(lbufPC, "S3%2x%8lx", &countN, &addrL ) != 2 )
+ return( 12 ); /* Flag error in S3 record */
+ oheadN = 5; /* 4 address + 1 checksum */
+ break;
+
+ default: /* ignore all but S1,2,3 records. */
+ return( 0 );
+ }
+
+ if( addrL > BUFSIZE ) return( 13 ); /* if address exceeds buffer size */
+ bufPC = bufAC + addrL; /* otherwise, point to right spot in buffer */
+
+ /* OK now see if checksum is OK, while reading data to buffer */
+ cksmB = 0;
+ countN++; /* Bump counter to read final checksum too */
+ for( i = 1; i <= countN; i++ )
+ {
+ sscanf( lbufPC + i*2, "%2x", &tvalN ); /* Scan a 2 hex digit byte */
+ cksmB += (unsigned char)tvalN;
+ if( ( i > oheadN ) && ( i < countN ) ) /* If scanned a data byte */
+ *bufPC++ = (unsigned char) tvalN; /* write it to the buffer */
+ }
+ if( cksmB += 1 ) return( 14 ); /* flag checksum error */
+
+ if( (bufPC - 1) > highestPC )
+ highestPC = bufPC - 1; /* track highest address loaded */
+
+ return( 0 ); /* Successful return */
+}
+
+
+
diff --git a/tools/hex2bin-2.0/src/binary.c b/tools/hex2bin-2.0/src/binary.c
new file mode 100644
index 0000000..63ead0d
--- /dev/null
+++ b/tools/hex2bin-2.0/src/binary.c
@@ -0,0 +1,196 @@
+/*---------------------------------------------------------------------------*
+ * binary.c *
+ * Copyright (C) 2014 Jacques Pelletier *
+ * *
+ * 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 2 *
+ * 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, write to the Free Software Foundation, *
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ *---------------------------------------------------------------------------*/
+
+#include <stdint.h>
+
+#include "binary.h"
+
+const uint8_t Reflect8[256] = {
+ 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0,
+ 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8,
+ 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4,
+ 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC,
+ 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2,
+ 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA,
+ 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6,
+ 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE,
+ 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1,
+ 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9,
+ 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5,
+ 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD,
+ 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3,
+ 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB,
+ 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7,
+ 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF,
+};
+
+uint16_t Reflect16(uint16_t Value16)
+{
+ return (((uint16_t) Reflect8[u16_lo(Value16)]) << 8) | ((uint16_t) Reflect8[u16_hi(Value16)]);
+}
+
+uint32_t Reflect24(uint32_t Value24)
+{
+ return (
+ (((uint32_t) Reflect8[u32_b0(Value24)]) << 16) |
+ (((uint32_t) Reflect8[u32_b1(Value24)]) << 8) |
+ ((uint32_t) Reflect8[u32_b2(Value24)])
+ );
+}
+
+uint32_t Reflect32(uint32_t Value32)
+{
+ return (
+ (((uint32_t) Reflect8[u32_b0(Value32)]) << 24) |
+ (((uint32_t) Reflect8[u32_b1(Value32)]) << 16) |
+ (((uint32_t) Reflect8[u32_b2(Value32)]) << 8) |
+ ((uint32_t) Reflect8[u32_b3(Value32)])
+ );
+}
+
+uint64_t Reflect40(uint64_t Value40)
+{
+ return (
+ (((uint64_t) Reflect8[u64_b0(Value40)]) << 32) |
+ (((uint64_t) Reflect8[u64_b1(Value40)]) << 24) |
+ (((uint64_t) Reflect8[u64_b2(Value40)]) << 16) |
+ (((uint64_t) Reflect8[u64_b3(Value40)]) << 8) |
+ ((uint64_t) Reflect8[u64_b4(Value40)])
+ );
+}
+
+uint64_t Reflect64(uint64_t Value64)
+{
+ return (
+ (((uint64_t) Reflect8[u64_b0(Value64)]) << 56) |
+ (((uint64_t) Reflect8[u64_b1(Value64)]) << 48) |
+ (((uint64_t) Reflect8[u64_b2(Value64)]) << 40) |
+ (((uint64_t) Reflect8[u64_b3(Value64)]) << 32) |
+ (((uint64_t) Reflect8[u64_b4(Value64)]) << 24) |
+ (((uint64_t) Reflect8[u64_b5(Value64)]) << 16) |
+ (((uint64_t) Reflect8[u64_b6(Value64)]) << 8) |
+ ((uint64_t) Reflect8[u64_b7(Value64)])
+ );
+}
+
+uint8_t u16_hi(uint16_t value)
+{
+ return (uint8_t)((value & 0xFF00) >> 8);
+}
+
+uint8_t u16_lo(uint16_t value)
+{
+ return (uint8_t)(value & 0x00FF);
+}
+
+uint8_t u32_b3(uint32_t value)
+{
+ return (uint8_t)((value & 0xFF000000) >> 24);
+}
+
+uint8_t u32_b2(uint32_t value)
+{
+ return (uint8_t)((value & 0x00FF0000) >> 16);
+}
+
+uint8_t u32_b1(uint32_t value)
+{
+ return (uint8_t)((value & 0x0000FF00) >> 8);
+}
+
+uint8_t u32_b0(uint32_t value)
+{
+ return (uint8_t)(value & 0x000000FF);
+}
+
+uint8_t u64_b7(uint64_t value)
+{
+ return (uint8_t)((value & 0xFF00000000000000) >> 56);
+}
+
+uint8_t u64_b6(uint64_t value)
+{
+ return (uint8_t)((value & 0x00FF000000000000) >> 48);
+}
+
+uint8_t u64_b5(uint64_t value)
+{
+ return (uint8_t)((value & 0x0000FF0000000000) >> 40);
+}
+
+uint8_t u64_b4(uint64_t value)
+{
+ return (uint8_t)((value & 0x000000FF00000000) >> 32);
+}
+
+uint8_t u64_b3(uint64_t value)
+{
+ return (uint8_t)((value & 0x00000000FF000000) >> 24);
+}
+
+uint8_t u64_b2(uint64_t value)
+{
+ return (uint8_t)((value & 0x0000000000FF0000) >> 16);
+}
+
+uint8_t u64_b1(uint64_t value)
+{
+ return (uint8_t)((value & 0x000000000000FF00) >> 8);
+}
+
+uint8_t u64_b0(uint64_t value)
+{
+ return (uint8_t)(value & 0x00000000000000FF);
+}
+
+/* Checksum/CRC conversion to ASCII */
+uint8_t nibble2ascii(uint8_t value)
+{
+ uint8_t result = value & 0x0f;
+
+ if (result > 9) return result + 0x41-0x0A;
+ else return result + 0x30;
+}
+
+bool cs_isdecdigit(char c)
+{
+ return (c >= 0x30) && (c < 0x3A);
+}
+
+unsigned char tohex(unsigned char c)
+{
+ if ((c >= '0') && (c < '9'+1))
+ return (c - '0');
+ if ((c >= 'A') && (c < 'F'+1))
+ return (c - 'A' + 0x0A);
+ if ((c >= 'a') && (c < 'f'+1))
+ return (c - 'a' + 0x0A);
+
+ return 0;
+}
+
+unsigned char todecimal(unsigned char c)
+{
+ if ((c >= '0') && (c < '9'+1))
+ return (c - '0');
+
+ return 0;
+}
+
+
diff --git a/tools/hex2bin-2.0/src/binary.h b/tools/hex2bin-2.0/src/binary.h
new file mode 100644
index 0000000..198589b
--- /dev/null
+++ b/tools/hex2bin-2.0/src/binary.h
@@ -0,0 +1,36 @@
+#ifndef _BINARY_H_
+#define _BINARY_H_
+
+typedef enum {false, true} bool;
+
+extern const unsigned char Reflect8[256];
+
+uint16_t Reflect16(uint16_t Value16);
+uint32_t Reflect24(uint32_t Value24);
+uint32_t Reflect32(uint32_t Value32);
+uint64_t Reflect40(uint64_t Value40);
+uint64_t Reflect64(uint64_t Value64);
+
+uint8_t u16_hi(uint16_t value);
+uint8_t u16_lo(uint16_t value);
+
+uint8_t u32_b3(uint32_t value);
+uint8_t u32_b2(uint32_t value);
+uint8_t u32_b1(uint32_t value);
+uint8_t u32_b0(uint32_t value);
+
+uint8_t u64_b7(uint64_t value);
+uint8_t u64_b6(uint64_t value);
+uint8_t u64_b5(uint64_t value);
+uint8_t u64_b4(uint64_t value);
+uint8_t u64_b3(uint64_t value);
+uint8_t u64_b2(uint64_t value);
+uint8_t u64_b1(uint64_t value);
+uint8_t u64_b0(uint64_t value);
+
+uint8_t nibble2ascii(uint8_t value);
+bool cs_isdecdigit(char c);
+unsigned char tohex(unsigned char c);
+unsigned char todecimal(unsigned char c);
+
+#endif /* _BINARY_H_ */
diff --git a/tools/hex2bin-2.0/src/common.c b/tools/hex2bin-2.0/src/common.c
new file mode 100644
index 0000000..f453dc4
--- /dev/null
+++ b/tools/hex2bin-2.0/src/common.c
@@ -0,0 +1,527 @@
+#include "common.h"
+
+filetype Filename; /* string for opening files */
+char Extension[MAX_EXTENSION_SIZE]; /* filename extension for output files */
+
+FILE *Filin, /* input files */
+ *Filout; /* output files */
+
+#ifdef USE_FILE_BUFFERS
+char *FilinBuf, /* text buffer for file input */
+ *FiloutBuf; /* text buffer for file output */
+#endif
+
+int Pad_Byte = 0xFF;
+bool Enable_Checksum_Error = false;
+bool Status_Checksum_Error = false;
+byte Checksum;
+unsigned int Record_Nb;
+
+/* This will hold binary codes translated from hex file. */
+byte *Memory_Block;
+unsigned int Lowest_Address, Highest_Address;
+unsigned int Starting_Address;
+unsigned int Max_Length = 0;
+unsigned int Minimum_Block_Size = 0x1000; // 4096 byte
+int Module;
+bool Minimum_Block_Size_Setted = false;
+bool Starting_Address_Setted = false;
+bool Max_Length_Setted = false;
+bool Swap_Wordwise = false;
+
+int Endian = 0;
+
+t_CRC Cks_Type = CHK8_SUM;
+unsigned int Cks_Start = 0, Cks_End = 0, Cks_Addr = 0, Cks_Value = 0;
+bool Cks_range_set = false;
+bool Cks_Addr_set = false;
+bool Force_Value = false;
+
+unsigned int Crc_Poly = 0x07, Crc_Init = 0, Crc_XorOut = 0;
+bool Crc_RefIn = false;
+bool Crc_RefOut = false;
+
+void usage(void)
+{
+ fprintf (stderr,
+ "\n"
+ "usage: %s [OPTIONS] filename\n"
+ "Options:\n"
+ " -c Enable record checksum verification\n"
+ " -C [Poly][Init][RefIn][RefOut][XorOut]\n CRC parameters"
+ " -e [ext] Output filename extension (without the dot)\n"
+ " -E [0|1] Endian for checksum/CRC, 0: little, 1: big\n"
+ " -f [address] Address of check result to write\n"
+ " -F [address] [value]\n Address and value to force\n"
+ " -k [0-4] Select check method (checksum or CRC) and size\n"
+ " -d display list of check methods/value size\n"
+ " -l [length] Maximal Length (Starting address + Length -1 is Max Address)\n"
+ " File will be filled with Pattern until Max Address is reached\n"
+ " -m [size] Minimum Block Size\n"
+ " File Size Dimension will be a multiple of Minimum block size\n"
+ " File will be filled with Pattern\n"
+ " Length must be a power of 2 in hexadecimal [see -l option]\n"
+ " Attention this option is STRONGER than Maximal Length \n"
+ " -p [value] Pad-byte value in hex (default: %x)\n"
+ " -r [start] [end]\n"
+ " Range to compute checksum over (default is min and max addresses)\n"
+ " -s [address] Starting address in hex (default: 0)\n"
+ " -w Swap wordwise (low <-> high)\n\n",
+ Pgm_Name,Pad_Byte);
+ exit(1);
+} /* procedure USAGE */
+
+void DisplayCheckMethods(void)
+{
+ fprintf (stderr,
+ "Check methods/value size:\n"
+ "0: Checksum 8-bit\n"
+ "1: Checksum 16-bit\n"
+ "2: CRC8\n"
+ "3: CRC16\n"
+ "4: CRC32\n");
+ exit(1);
+}
+
+#define LAST_CHECK_METHOD 4
+
+void *NoFailMalloc (size_t size)
+{
+ void *result;
+
+ if ((result = malloc (size)) == NULL)
+ {
+ fprintf (stderr,"Can't allocate memory.\n");
+ exit(1);
+ }
+ return (result);
+}
+
+/* Open the input file, with error checking */
+void NoFailOpenInputFile (char *Flnm)
+{
+ while ((Filin = fopen(Flnm,"r")) == NULL)
+ {
+ fprintf (stderr,"Input file %s cannot be opened. Enter new filename: ",Flnm);
+ if (fgets (Flnm,MAX_FILE_NAME_SIZE,stdin) == NULL) exit(1); /* modified error checking */
+
+ if (Flnm[strlen(Flnm) - 1] == '\n') Flnm[strlen(Flnm) - 1] = '\0';
+ }
+
+#ifdef USE_FILE_BUFFERS
+ FilinBuf = (char *) NoFailMalloc (BUFFSZ);
+ setvbuf(Filin, FilinBuf, _IOFBF, BUFFSZ);
+#endif
+} /* procedure OPENFILIN */
+
+/* Open the output file, with error checking */
+void NoFailOpenOutputFile (char *Flnm)
+{
+ while ((Filout = fopen(Flnm,"wb")) == NULL)
+ {
+ /* Failure to open the output file may be
+ simply due to an insufficiant permission setting. */
+ fprintf(stderr,"Output file %s cannot be opened. Enter new file name: ", Flnm);
+ if (fgets(Flnm,MAX_FILE_NAME_SIZE,stdin) == NULL) exit(1);
+
+ if (Flnm[strlen(Flnm) - 1] == '\n') Flnm[strlen(Flnm) - 1] = '\0';
+ }
+
+#ifdef USE_FILE_BUFFERS
+ FiloutBuf = (char *) NoFailMalloc (BUFFSZ);
+ setvbuf(Filout, FiloutBuf, _IOFBF, BUFFSZ);
+#endif
+
+} /* procedure OPENFILOUT */
+
+void GetLine(char* str,FILE *in)
+{
+ char *result;
+
+ result = fgets(str,MAX_LINE_SIZE,in);
+ if ((result == NULL) && !feof (in)) fprintf(stderr,"Error occurred while reading from file\n");
+}
+
+// 0 or 1
+int GetBin(const char *str)
+{
+ int result;
+ unsigned int value;
+
+ result = sscanf(str,"%u",&value);
+
+ if (result == 1) return value & 1;
+ else
+ {
+ fprintf(stderr,"GetBin: some error occurred when parsing options.\n");
+ exit (1);
+ }
+}
+
+int GetDec(const char *str)
+{
+ int result;
+ unsigned int value;
+
+ result = sscanf(str,"%u",&value);
+
+ if (result == 1) return value;
+ else
+ {
+ fprintf(stderr,"GetDec: some error occurred when parsing options.\n");
+ exit (1);
+ }
+}
+
+int GetHex(const char *str)
+{
+ int result;
+ unsigned int value;
+
+ result = sscanf(str,"%x",&value);
+
+ if (result == 1) return value;
+ else
+ {
+ fprintf(stderr,"GetHex: some error occurred when parsing options.\n");
+ exit (1);
+ }
+}
+
+// Char t/T: true f/F: false
+bool GetBoolean(const char *str)
+{
+ int result;
+ unsigned char value, temp;
+
+ result = sscanf(str,"%c",&value);
+ temp = tolower(value);
+
+ if ((result == 1) && ((temp == 't') || (temp == 'f')))
+ {
+ return (temp == 't');
+ }
+ else
+ {
+ fprintf(stderr,"GetBoolean: some error occurred when parsing options.\n");
+ exit (1);
+ }
+}
+
+void GetExtension(const char *str,char *ext)
+{
+ if (strlen(str) > MAX_EXTENSION_SIZE)
+ usage();
+
+ strcpy(ext, str);
+}
+
+/* Adds an extension to a file name */
+void PutExtension(char *Flnm, char *Extension)
+{
+ char *Period; /* location of period in file name */
+ bool Samename;
+
+ /* This assumes DOS like file names */
+ /* Don't use strchr(): consider the following filename:
+ ../my.dir/file.hex
+ */
+ if ((Period = strrchr(Flnm,'.')) != NULL)
+ *(Period) = '\0';
+
+ Samename = false;
+ if (strcmp(Extension, Period+1) == 0)
+ Samename = true;
+
+ strcat(Flnm,".");
+ strcat(Flnm, Extension);
+ if (Samename)
+ {
+ fprintf (stderr,"Input and output filenames (%s) are the same.", Flnm);
+ exit(1);
+ }
+}
+
+void VerifyChecksumValue(void)
+{
+ if ((Checksum != 0) && Enable_Checksum_Error)
+ {
+ fprintf(stderr,"Checksum error in record %d: should be %02X\n",
+ Record_Nb, (256 - Checksum) & 0xFF);
+ Status_Checksum_Error = true;
+ }
+}
+
+void CrcParamsCheck(void)
+{
+ switch (Cks_Type)
+ {
+ case CRC8:
+ Crc_Poly &= 0xFF;
+ Crc_Init &= 0xFF;
+ Crc_XorOut &= 0xFF;
+ break;
+ case CRC16:
+ Crc_Poly &= 0xFFFF;
+ Crc_Init &= 0xFFFF;
+ Crc_XorOut &= 0xFFFF;
+ break;
+ case CRC32:
+ break;
+ default:
+ fprintf (stderr,"See file CRC list.txt for parameters\n");
+ exit(1);
+ }
+}
+
+void WriteMemBlock16(uint16_t Value)
+{
+ if (Endian == 1)
+ {
+ Memory_Block[Cks_Addr - Lowest_Address] = u16_hi(Value);
+ Memory_Block[Cks_Addr - Lowest_Address +1] = u16_lo(Value);
+ }
+ else
+ {
+ Memory_Block[Cks_Addr - Lowest_Address +1] = u16_hi(Value);
+ Memory_Block[Cks_Addr - Lowest_Address] = u16_lo(Value);
+ }
+}
+
+void WriteMemBlock32(uint32_t Value)
+{
+ if (Endian == 1)
+ {
+ Memory_Block[Cks_Addr - Lowest_Address] = u32_b3(Value);
+ Memory_Block[Cks_Addr - Lowest_Address +1] = u32_b2(Value);
+ Memory_Block[Cks_Addr - Lowest_Address +2] = u32_b1(Value);
+ Memory_Block[Cks_Addr - Lowest_Address +3] = u32_b0(Value);
+ }
+ else
+ {
+ Memory_Block[Cks_Addr - Lowest_Address +3] = u32_b3(Value);
+ Memory_Block[Cks_Addr - Lowest_Address +2] = u32_b2(Value);
+ Memory_Block[Cks_Addr - Lowest_Address +1] = u32_b1(Value);
+ Memory_Block[Cks_Addr - Lowest_Address] = u32_b0(Value);
+ }
+}
+
+void WriteMemory(void)
+{
+ if ((Cks_Addr >= Lowest_Address) || (Cks_Addr < Highest_Address))
+ {
+ if(Force_Value)
+ {
+ switch (Cks_Type)
+ {
+ case 0:
+ Memory_Block[Cks_Addr - Lowest_Address] = Cks_Value;
+ fprintf(stdout,"Addr %08X set to %02X\n",Cks_Addr, Cks_Value);
+ break;
+ case 1:
+ WriteMemBlock16(Cks_Value);
+ fprintf(stdout,"Addr %08X set to %04X\n",Cks_Addr, Cks_Value);
+ break;
+ case 2:
+ WriteMemBlock32(Cks_Value);
+ fprintf(stdout,"Addr %08X set to %08X\n",Cks_Addr, Cks_Value);
+ break;
+ default:;
+ }
+ }
+ else if (Cks_Addr_set)
+ {
+ /* Add a checksum to the binary file */
+ if (!Cks_range_set)
+ {
+ Cks_Start = Lowest_Address;
+ Cks_End = Highest_Address;
+ }
+ /* checksum range MUST BE in the array bounds */
+
+ if (Cks_Start < Lowest_Address)
+ {
+ fprintf(stdout,"Modifying range start from %X to %X\n",Cks_Start,Lowest_Address);
+ Cks_Start = Lowest_Address;
+ }
+ if (Cks_End > Highest_Address)
+ {
+ fprintf(stdout,"Modifying range end from %X to %X\n",Cks_End,Highest_Address);
+ Cks_End = Highest_Address;
+ }
+
+ switch (Cks_Type)
+ {
+ case CHK8_SUM:
+ {
+ uint8_t wCKS = 0;
+
+ for (unsigned int i=Cks_Start; i<=Cks_End; i++)
+ {
+ wCKS += Memory_Block[i - Lowest_Address];
+ }
+
+ fprintf(stdout,"8-bit Checksum = %02X\n",wCKS & 0xff);
+ Memory_Block[Cks_Addr - Lowest_Address] = wCKS;
+ fprintf(stdout,"Addr %08X set to %02X\n",Cks_Addr, wCKS);
+ }
+ break;
+
+ case CHK16:
+ {
+ uint16_t wCKS, w;
+
+ wCKS = 0;
+
+ if (Endian == 1)
+ {
+ for (unsigned int i=Cks_Start; i<=Cks_End; i+=2)
+ {
+ w = Memory_Block[i - Lowest_Address +1] | ((word)Memory_Block[i - Lowest_Address] << 8);
+ wCKS += w;
+ }
+ }
+ else
+ {
+ for (unsigned int i=Cks_Start; i<=Cks_End; i+=2)
+ {
+ w = Memory_Block[i - Lowest_Address] | ((word)Memory_Block[i - Lowest_Address +1] << 8);
+ wCKS += w;
+ }
+ }
+ fprintf(stdout,"16-bit Checksum = %04X\n",wCKS);
+ WriteMemBlock16(wCKS);
+ fprintf(stdout,"Addr %08X set to %04X\n",Cks_Addr, wCKS);
+ }
+ break;
+
+ case CRC8:
+ {
+ uint8_t CRC8;
+ crc_table = NoFailMalloc(256);
+
+ if (Crc_RefIn)
+ {
+ init_crc8_reflected_tab(Reflect8[Crc_Poly]);
+ CRC8 = Reflect8[Crc_Init];
+ }
+ else
+ {
+ init_crc8_normal_tab(Crc_Poly);
+ CRC8 = Crc_Init;
+ }
+
+ for (unsigned int i=Cks_Start; i<=Cks_End; i++)
+ {
+ CRC8 = update_crc8(CRC8,Memory_Block[i - Lowest_Address]);
+ }
+
+ CRC8 = (CRC8 ^ Crc_XorOut) & 0xff;
+ Memory_Block[Cks_Addr - Lowest_Address] = CRC8;
+ fprintf(stdout,"Addr %08X set to %02X\n",Cks_Addr, CRC8);
+ }
+ break;
+
+ case CRC16:
+ {
+ uint16_t CRC16;
+ crc_table = NoFailMalloc(256 * 2);
+
+ if (Crc_RefIn)
+ {
+ init_crc16_reflected_tab(Reflect16(Crc_Poly));
+ CRC16 = Reflect16(Crc_Init);
+
+ for (unsigned int i=Cks_Start; i<=Cks_End; i++)
+ {
+ CRC16 = update_crc16_reflected(CRC16,Memory_Block[i - Lowest_Address]);
+ }
+ }
+ else
+ {
+ init_crc16_normal_tab(Crc_Poly);
+ CRC16 = Crc_Init;
+
+
+ for (unsigned int i=Cks_Start; i<=Cks_End; i++)
+ {
+ CRC16 = update_crc16_normal(CRC16,Memory_Block[i - Lowest_Address]);
+ }
+ }
+
+ CRC16 = (CRC16 ^ Crc_XorOut) & 0xffff;
+ WriteMemBlock16(CRC16);
+ fprintf(stdout,"Addr %08X set to %04X\n",Cks_Addr, CRC16);
+ }
+ break;
+
+ case CRC32:
+ {
+ uint32_t CRC32;
+
+ crc_table = NoFailMalloc(256 * 4);
+ if (Crc_RefIn)
+ {
+ init_crc32_reflected_tab(Reflect32(Crc_Poly));
+ CRC32 = Reflect32(Crc_Init);
+
+ for (unsigned int i=Cks_Start; i<=Cks_End; i++)
+ {
+ CRC32 = update_crc32_reflected(CRC32,Memory_Block[i - Lowest_Address]);
+ }
+ }
+ else
+ {
+ init_crc32_normal_tab(Crc_Poly);
+ CRC32 = Crc_Init;
+
+ for (unsigned int i=Cks_Start; i<=Cks_End; i++)
+ {
+ CRC32 = update_crc32_normal(CRC32,Memory_Block[i - Lowest_Address]);
+ }
+ }
+
+ CRC32 ^= Crc_XorOut;
+ WriteMemBlock32(CRC32);
+ fprintf(stdout,"Addr %08X set to %08X\n",Cks_Addr, CRC32);
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ free(crc_table);
+ }
+ }
+ else
+ {
+ fprintf (stderr,"Force/Check address outside of memory range\n");
+ }
+
+ /* write binary file */
+ fwrite (Memory_Block,
+ Max_Length,
+ 1,
+ Filout);
+
+ free (Memory_Block);
+
+ // Minimum_Block_Size is set; the memory buffer is multiple of this?
+ if (Minimum_Block_Size_Setted==true)
+ {
+ Module = Max_Length % Minimum_Block_Size;
+ if (Module)
+ {
+ Memory_Block = (byte *) NoFailMalloc(Module);
+ memset (Memory_Block,Pad_Byte,Module);
+ fwrite (Memory_Block,
+ Module,
+ 1,
+ Filout);
+ free (Memory_Block);
+ if (Max_Length_Setted==true)
+ fprintf(stdout,"Attention Max Length changed by Minimum Block Size\n");
+ }
+ }
+}
diff --git a/tools/hex2bin-2.0/src/common.h b/tools/hex2bin-2.0/src/common.h
new file mode 100644
index 0000000..64caff5
--- /dev/null
+++ b/tools/hex2bin-2.0/src/common.h
@@ -0,0 +1,116 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "binary.h"
+#include "libcrc.h"
+
+/* To compile with Microsoft Visual Studio */
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#if defined(MSDOS) || defined(__DOS__) || defined(__MSDOS__) || defined(_MSDOS)
+#define _IS_OPTION_(x) (((x) == '-') || ((x) == '/'))
+#else
+/* Assume unix and similar */
+/* We don't accept an option beginning with a '/' because it could be a file name. */
+#define _IS_OPTION_(x) ((x) == '-')
+#endif
+
+/* We use buffer to speed disk access. */
+#ifdef USE_FILE_BUFFERS
+#define BUFFSZ 4096
+#endif
+
+/* FIXME how to get it from the system/OS? */
+#define MAX_FILE_NAME_SIZE 81
+
+#ifdef DOS
+#define MAX_EXTENSION_SIZE 4
+#else
+#define MAX_EXTENSION_SIZE 16
+#endif
+
+/* The data records can contain 255 bytes: this means 512 characters. */
+#define MAX_LINE_SIZE 1024
+
+typedef char filetype[MAX_FILE_NAME_SIZE];
+typedef unsigned char byte;
+typedef unsigned short word;
+
+#define LAST_CHECK_METHOD 4
+
+typedef enum Crc
+{
+ CHK8_SUM =0,
+ CHK16,
+ CRC8,
+ CRC16,
+ CRC32
+} t_CRC;
+
+extern const char *Pgm_Name;
+void usage(void);
+void DisplayCheckMethods(void);
+
+void *NoFailMalloc (size_t size);
+void NoFailOpenInputFile (char *Flnm);
+void NoFailOpenOutputFile (char *Flnm);
+void GetLine(char* str,FILE *in);
+int GetBin(const char *str);
+int GetDec(const char *str);
+int GetHex(const char *str);
+bool GetBoolean(const char *str);
+void GetExtension(const char *str,char *ext);
+void PutExtension(char *Flnm, char *Extension);
+
+filetype Filename; /* string for opening files */
+char Extension[MAX_EXTENSION_SIZE]; /* filename extension for output files */
+
+FILE *Filin, /* input files */
+ *Filout; /* output files */
+
+#ifdef USE_FILE_BUFFERS
+char *FilinBuf, /* text buffer for file input */
+ *FiloutBuf; /* text buffer for file output */
+#endif
+
+int Pad_Byte;
+bool Enable_Checksum_Error;
+bool Status_Checksum_Error;
+byte Checksum;
+unsigned int Record_Nb;
+
+/* This will hold binary codes translated from hex file. */
+byte *Memory_Block;
+unsigned int Lowest_Address, Highest_Address;
+unsigned int Starting_Address;
+unsigned int Max_Length;
+unsigned int Minimum_Block_Size;
+int Module;
+bool Minimum_Block_Size_Setted;
+bool Starting_Address_Setted;
+bool Max_Length_Setted;
+bool Swap_Wordwise;
+
+int Endian;
+
+t_CRC Cks_Type;
+unsigned int Cks_Start, Cks_End, Cks_Addr, Cks_Value;
+bool Cks_range_set;
+bool Cks_Addr_set;
+bool Force_Value;
+
+unsigned int Crc_Poly, Crc_Init, Crc_XorOut;
+bool Crc_RefIn;
+bool Crc_RefOut;
+
+void VerifyChecksumValue(void);
+void CrcParamsCheck(void);
+void WriteMemBlock16(uint16_t Value);
+void WriteMemBlock32(uint32_t Value);
+void WriteMemory(void);
+
diff --git a/tools/hex2bin-2.0/src/hex2bin.1 b/tools/hex2bin-2.0/src/hex2bin.1
new file mode 100644
index 0000000..c2e5e89
--- /dev/null
+++ b/tools/hex2bin-2.0/src/hex2bin.1
@@ -0,0 +1,294 @@
+.\" Automatically generated by Pod::Man 2.27 (Pod::Simple 3.28)
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings. \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote. \*(C+ will
+.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
+.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
+.\" nothing in troff, for use with C<>.
+.tr \(*W-
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+. ds -- \(*W-
+. ds PI pi
+. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+. ds L" ""
+. ds R" ""
+. ds C` ""
+. ds C' ""
+'br\}
+.el\{\
+. ds -- \|\(em\|
+. ds PI \(*p
+. ds L" ``
+. ds R" ''
+. ds C`
+. ds C'
+'br\}
+.\"
+.\" Escape single quotes in literal strings from groff's Unicode transform.
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
+.\" entries marked with X<> in POD. Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.\"
+.\" Avoid warning from groff about undefined register 'F'.
+.de IX
+..
+.nr rF 0
+.if \n(.g .if rF .nr rF 1
+.if (\n(rF:(\n(.g==0)) \{
+. if \nF \{
+. de IX
+. tm Index:\\$1\t\\n%\t"\\$2"
+..
+. if !\nF==2 \{
+. nr % 0
+. nr F 2
+. \}
+. \}
+.\}
+.rr rF
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear. Run. Save yourself. No user-serviceable parts.
+. \" fudge factors for nroff and troff
+.if n \{\
+. ds #H 0
+. ds #V .8m
+. ds #F .3m
+. ds #[ \f1
+. ds #] \fP
+.\}
+.if t \{\
+. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+. ds #V .6m
+. ds #F 0
+. ds #[ \&
+. ds #] \&
+.\}
+. \" simple accents for nroff and troff
+.if n \{\
+. ds ' \&
+. ds ` \&
+. ds ^ \&
+. ds , \&
+. ds ~ ~
+. ds /
+.\}
+.if t \{\
+. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+. \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+. \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+. \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+. ds : e
+. ds 8 ss
+. ds o a
+. ds d- d\h'-1'\(ga
+. ds D- D\h'-1'\(hy
+. ds th \o'bp'
+. ds Th \o'LP'
+. ds ae ae
+. ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "HEX2BIN 1"
+.TH HEX2BIN 1 "2015-02-28" "perl v5.18.2" "User Contributed Perl Documentation"
+.\" For nroff, turn off justification. Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.if n .ad l
+.nh
+.SH "NAME"
+hex2bin/mot2bin \e\- converts Intel/Motorola hex files into binary
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+hex2bin [options] file
+.PP
+Option list:
+ [\-c]
+ [\-C Poly Init RefIn RefOut XorOut]
+ [\-e extension]
+ [\-E 0|1]
+ [\-f address]
+ [\-F address value]
+ [\-k checksum type]
+ [\-l length]
+ [\-m minimum block size]
+ [\-p pad byte]
+ [\-r start end]
+ [\-s address]
+ [\-w]
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+\&\fBHex2bin\fR
+is a program that converts an Intel hex format into binary.
+It can handle the extended Intel hex format. Both the segmented
+and linear address records are supported.
+Records need not be sorted and there can be gaps between records.
+Records are assumed to be non-overlapping.
+Padding bytes may be specified and a checksum may be inserted in the
+binary file.
+.PP
+\&\fBMot2bin\fR
+does the same with Motorola hex files. It has the same features and command line
+options. 24 bit and 32 bit records are supported.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+Options can be specified in any order, with the file name at the end. Options are
+now case sensitive. All option values are specified in hexadecimal.
+.PP
+\&\fB\-c\fR
+.PP
+Enables checksum verification.
+.PP
+By default, it ignores checksum errors in the hex file, so that someone can change
+by hand some bytes with a text editor, allowing quick fixes without recompiling a source
+code all over again. This is useful when tweaking constants directly in the code or
+something similar. If you want checksum error reporting, specify the option \-c.
+.PP
+\&\fBEx.: hex2bin \-c example.hex\fR
+.PP
+If there is a checksum error somewhere, the program will continue the
+conversion anyway.
+.PP
+\&\fB\-C Poly Init RefIn RefOut XorOut\fR
+.PP
+\&\s-1CRC\s0 parameters. See the doc/CRC list.txt file for a description of common CRCs. See also
+the test/Makefile for examples of command lines. Needs \fB\-k\fR and \fB\-f\fR option.
+RefIn and RefOut parameters are specified by \fBt\fR or \fBf\fR for true or false.
+.PP
+\&\fB\-d\fR
+.PP
+Display the list of available check methods and sizes.
+.PP
+\&\fB\-e extension\fR
+.PP
+By default, the output file will have an extension \fBfilename.bin\fR.
+Another extension may be specified with this command:
+.PP
+\&\fBEx.: hex2bin \-e com example.hex\fR
+.PP
+The output file will be example.com
+.PP
+\&\fB\-E 0|1\fR
+.PP
+Endianness for writing the check result or forcing a 16\-bit value.
+ \fB0\fR: little, \fB1\fR: big.
+.PP
+By default, little endian is used.
+.PP
+\&\fB\-f address\fR
+.PP
+Address in hexadecimal for inserting the check value in the binary file. Needs \fB\-k\fR
+option to specify the check method. A range can be specified with option \fB\-r\fR.
+.PP
+\&\fB\-F address value\fR
+.PP
+Address and value of checksum to insert (force) in the binary file. Needs \fB\-k\fR
+option to specify the size. The value is written as is.
+.PP
+\&\fB\-k 0\-4\fR
+.PP
+In many cases, someone needs to insert a check value in the binary file. For example,
+a boot rom is programmed with a checksum which is verified at power-up. This feature
+uses also options \fB\-r\fR, \fB\-C\fR and \fB\-f\fR. Display the list with \fB\-d\fR.
+.PP
+Select the checksum type to insert into the binary file
+ 0: Checksum 8\-bit
+ 1: Checksum 16\-bit
+ 2: \s-1CRC8
+ 3: CRC16
+ 4: CRC32\s0
+.PP
+\&\fB\-l length\fR
+.PP
+The binary file will be padded with \s-1FF\s0 or pad bytes as specified by the option
+below, up to a maximal Length (Starting address + Length \-1 is Max Address)
+.PP
+\&\fB\-m minimum_block_size\fR
+.PP
+File Size Dimension will be a multiple of Minimum block size.
+File will be filled with Pattern.
+Length must be a power of 2 in hexadecimal [see \fB\-l\fR option]
+Attention this option is \s-1STRONGER\s0 than Maximal Length
+.PP
+\&\fB\-p pad_byte\fR
+.PP
+Pads unused locations with the specified byte.
+.PP
+By default, this byte is \s-1FF,\s0 which is the unprogrammed value for most EPROM/EEPROM/Flash.
+.PP
+\&\fBEx.: hex2bin \-p 3E example.hex\fR
+.PP
+\&\fB\-r [start] [end]\fR
+.PP
+Range to compute binary checksum over (default is min and max addresses)
+.PP
+\&\fB\-s address\fR
+.PP
+Specify the starting address of the binary file.
+.PP
+Normally, hex2bin will generate a binary file starting at the lowest address in
+the hex file. If the lowest address isn't 0000, ex: 0100, the first byte that
+should be at 0100 will be stored at address 0000 in the binary file. This may
+cause problems when using the binary file to program an \s-1EPROM.\s0
+.PP
+If you can't specify the starting address (or offset) to your \s-1EPROM\s0 programmer,
+you can specify a starting address on the command line:
+.PP
+\&\fBEx.: hex2bin \-s 0000 records_start_at_0100.hex\fR
+.PP
+The bytes will be stored in the binary file with a padding from 0000 to the
+lowest address (00FF in this case). Padding bytes are all \s-1FF\s0 by default so an \s-1EPROM\s0
+programmer can skip these bytes when programming. The padding value can be changed
+with the \-p option.
+.PP
+\&\fB\-w\fR
+.PP
+Swap wordwise (low <\-> high). Used by Microchip's \s-1MPLAB IDE\s0
+.SH "NOTES"
+.IX Header "NOTES"
+This program does minimal error checking since many hex files are
+generated by known good assemblers.
+.SH "AUTHOR Jacques Pelletier (jpelletier@ieee.org) \- version 2.0"
+.IX Header "AUTHOR Jacques Pelletier (jpelletier@ieee.org) - version 2.0"
diff --git a/tools/hex2bin-2.0/src/hex2bin.c b/tools/hex2bin-2.0/src/hex2bin.c
new file mode 100644
index 0000000..7537d8c
--- /dev/null
+++ b/tools/hex2bin-2.0/src/hex2bin.c
@@ -0,0 +1,587 @@
+/*
+ hex2bin converts an Intel hex file to binary.
+
+ Copyright (C) 2015, Jacques Pelletier
+ checksum extensions Copyright (C) 2004 Rockwell Automation
+ 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.
+ 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.
+
+ 20040617 Alf Lacis: Added pad byte (may not always want FF).
+ Added 'break;' to remove GNU compiler warning about label at
+ end of compound statement
+ Added PROGRAM & VERSION strings.
+
+ 20071005 PG: Improvements on options parsing
+ 20091212 JP: Corrected crash on 0 byte length data records
+ 20100402 JP: Corrected bug on physical address calculation for extended
+ linear address record.
+ ADDRESS_MASK is now calculated from MEMORY_SIZE
+
+ 20120125 Danny Schneider:
+ Added code for filling a binary file to a given Max_Length relative to
+ Starting Address if Max-Address is larger than Highest-Address
+ 20120509 Yoshimasa Nakane:
+ modified error checking (also for output file, JP)
+ 20141005 JP: added support for byte swapped hex files
+ corrected bug caused by extra LF at end or within file
+ 20141008 JP: removed junk code
+ 20141121 Slucx: added line for removing extra CR when entering file name at run time.
+ 20141122 Simone Fratini: small feature added
+ 20150116 Richard Genoud (Paratronic): correct buffer overflows/wrong results with the -l flag
+ 20150122 JP: added support for different check methods
+ 20150221 JP: rewrite of the checksum write/force value
+*/
+
+#define PROGRAM "hex2bin"
+#define VERSION "2.0"
+
+#include "common.h"
+
+#define NO_ADDRESS_TYPE_SELECTED 0
+#define LINEAR_ADDRESS 1
+#define SEGMENTED_ADDRESS 2
+
+const char *Pgm_Name = PROGRAM;
+
+int main (int argc, char *argv[])
+{
+ /* line inputted from file */
+ char Line[MAX_LINE_SIZE];
+
+ /* flag that a file was read */
+ bool Fileread;
+
+ /* cmd-line parameter # */
+ char *p;
+
+ int Param,result;
+
+ /* Application specific */
+
+ unsigned int Nb_Bytes;
+ unsigned int First_Word, Address, Segment, Upper_Address;
+ unsigned int Phys_Addr, Type;
+ unsigned int temp;
+ unsigned int Records_Start; // Lowest address of the records
+
+ /* We will assume that when one type of addressing is selected, it will be valid for all the
+ current file. Records for the other type will be ignored. */
+ unsigned int Seg_Lin_Select = NO_ADDRESS_TYPE_SELECTED;
+
+ unsigned int temp2;
+
+ byte Data_Str[MAX_LINE_SIZE];
+
+ fprintf (stdout,PROGRAM" v"VERSION", Copyright (C) 2015 Jacques Pelletier & contributors\n\n");
+
+ if (argc == 1)
+ usage();
+
+ strcpy(Extension, "bin"); /* default is for binary file extension */
+
+ /* read file */
+ Starting_Address = 0;
+
+ /*
+ use p for parsing arguments
+ use i for number of parameters to skip
+ use c for the current option
+ */
+ for (Param = 1; Param < argc; Param++)
+ {
+ int i = 0;
+ char c;
+
+ p = argv[Param];
+ c = *(p+1); /* Get option character */
+
+ if ( _IS_OPTION_(*p) )
+ {
+ // test for no space between option and parameter
+ if (strlen(p) != 2) usage();
+
+ switch(c)
+ {
+ /* file extension */
+ case 'c':
+ Enable_Checksum_Error = true;
+ i = 0;
+ break;
+ case 'd':
+ DisplayCheckMethods();
+ case 'e':
+ GetExtension(argv[Param + 1],Extension);
+ i = 1; /* add 1 to Param */
+ break;
+ case 'E':
+ Endian = GetBin(argv[Param + 1]);
+ i = 1; /* add 1 to Param */
+ break;
+ case 'f':
+ Cks_Addr = GetHex(argv[Param + 1]);
+ Cks_Addr_set = true;
+ i = 1; /* add 1 to Param */
+ break;
+ case 'F':
+ Cks_Addr = GetHex(argv[Param + 1]);
+ Cks_Value = GetHex(argv[Param + 2]);
+ Force_Value = true;
+ i = 2; /* add 2 to Param */
+ break;
+ case 'k':
+ Cks_Type = GetHex(argv[Param + 1]);
+ {
+ if (Cks_Type > LAST_CHECK_METHOD) usage();
+ }
+ i = 1; /* add 1 to Param */
+ break;
+ case 'l':
+ Max_Length = GetHex(argv[Param + 1]);
+ if (Max_Length > 0x800000)
+ {
+ fprintf(stderr,"Max_Length = %u\n", Max_Length);
+ exit(1);
+ }
+ Max_Length_Setted = true;
+ i = 1; /* add 1 to Param */
+ break;
+ case 'm':
+ Minimum_Block_Size = GetHex(argv[Param + 1]);
+ Minimum_Block_Size_Setted = true;
+ i = 1; /* add 1 to Param */
+ break;
+ case 'p':
+ Pad_Byte = GetHex(argv[Param + 1]);
+ i = 1; /* add 1 to Param */
+ break;
+ case 'r':
+ Cks_Start = GetHex(argv[Param + 1]);
+ Cks_End = GetHex(argv[Param + 2]);
+ Cks_range_set = true;
+ i = 2; /* add 2 to Param */
+ break;
+ case 's':
+ Starting_Address = GetHex(argv[Param + 1]);
+ Starting_Address_Setted = true;
+ i = 1; /* add 1 to Param */
+ break;
+ case 'w':
+ Swap_Wordwise = true;
+ i = 0;
+ break;
+ case 'C':
+ Crc_Poly = GetHex(argv[Param + 1]);
+ Crc_Init = GetHex(argv[Param + 2]);
+ Crc_RefIn = GetBoolean(argv[Param + 3]);
+ Crc_RefOut = GetBoolean(argv[Param + 4]);
+ Crc_XorOut = GetHex(argv[Param + 5]);
+ CrcParamsCheck();
+ i = 5; /* add 5 to Param */
+ break;
+
+ case '?':
+ case 'h':
+ default:
+ usage();
+ } /* switch */
+
+ /* Last parameter is not a filename */
+ if (Param == argc-1) usage();
+
+ /* if (Param + i) < (argc -1) */
+ if (Param < argc -1 -i) Param += i;
+ else usage();
+
+ }
+ else
+ break;
+ /* if option */
+ } /* for Param */
+
+ /* when user enters input file name */
+
+ /* Assume last parameter is filename */
+ strcpy(Filename,argv[argc -1]);
+
+ /* Just a normal file name */
+ NoFailOpenInputFile (Filename);
+ PutExtension(Filename, Extension);
+ NoFailOpenOutputFile(Filename);
+ Fileread = true;
+
+ /* To begin, assume the lowest address is at the end of the memory.
+ While reading each records, subsequent addresses will lower this number.
+ At the end of the input file, this value will be the lowest address.
+
+ A similar assumption is made for highest address. It starts at the
+ beginning of memory. While reading each records, subsequent addresses will raise this number.
+ At the end of the input file, this value will be the highest address. */
+ Lowest_Address = (unsigned int)-1;
+ Highest_Address = 0;
+ Records_Start = 0;
+ Segment = 0;
+ Upper_Address = 0;
+ Record_Nb = 0; // Used for reporting errors
+
+ /* get highest and lowest addresses so that we can allocate the right size */
+ do
+ {
+ unsigned int i;
+
+ /* Read a line from input file. */
+ GetLine(Line,Filin);
+ Record_Nb++;
+
+ /* Remove carriage return/line feed at the end of line. */
+ i = strlen(Line);
+
+ if (--i != 0)
+ {
+ if (Line[i] == '\n') Line[i] = '\0';
+
+ /* Scan the first two bytes and nb of bytes.
+ The two bytes are read in First_Word since its use depend on the
+ record type: if it's an extended address record or a data record.
+ */
+ result = sscanf (Line, ":%2x%4x%2x%s",&Nb_Bytes,&First_Word,&Type,Data_Str);
+ if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+
+ p = (char *) Data_Str;
+
+ /* If we're reading the last record, ignore it. */
+ switch (Type)
+ {
+ /* Data record */
+ case 0:
+ if (Nb_Bytes == 0)
+ break;
+
+ Address = First_Word;
+
+ if (Seg_Lin_Select == SEGMENTED_ADDRESS)
+ {
+ Phys_Addr = (Segment << 4) + Address;
+ }
+ else
+ {
+ /* LINEAR_ADDRESS or NO_ADDRESS_TYPE_SELECTED
+ Upper_Address = 0 as specified in the Intel spec. until an extended address
+ record is read. */
+ Phys_Addr = ((Upper_Address << 16) + Address);
+ }
+
+ /* Set the lowest address as base pointer. */
+ if (Phys_Addr < Lowest_Address)
+ Lowest_Address = Phys_Addr;
+
+ /* Same for the top address. */
+ temp = Phys_Addr + Nb_Bytes -1;
+
+ if (temp > Highest_Address)
+ Highest_Address = temp;
+
+ break;
+
+ case 2:
+ /* First_Word contains the offset. It's supposed to be 0000 so
+ we ignore it. */
+
+ /* First extended segment address record ? */
+ if (Seg_Lin_Select == NO_ADDRESS_TYPE_SELECTED)
+ Seg_Lin_Select = SEGMENTED_ADDRESS;
+
+ /* Then ignore subsequent extended linear address records */
+ if (Seg_Lin_Select == SEGMENTED_ADDRESS)
+ {
+ result = sscanf (p, "%4x%2x",&Segment,&temp2);
+ if (result != 2) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+
+ /* Update the current address. */
+ Phys_Addr = (Segment << 4);
+ }
+ else
+ {
+ fprintf(stderr,"Ignored extended linear address record %d\n", Record_Nb);
+ }
+ break;
+
+ case 4:
+ /* First_Word contains the offset. It's supposed to be 0000 so
+ we ignore it. */
+
+ /* First extended linear address record ? */
+ if (Seg_Lin_Select == NO_ADDRESS_TYPE_SELECTED)
+ Seg_Lin_Select = LINEAR_ADDRESS;
+
+ /* Then ignore subsequent extended segment address records */
+ if (Seg_Lin_Select == LINEAR_ADDRESS)
+ {
+ result = sscanf (p, "%4x%2x",&Upper_Address,&temp2);
+ if (result != 2) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+
+ /* Update the current address. */
+ Phys_Addr = (Upper_Address << 16);
+ }
+ else
+ {
+ fprintf(stderr,"Ignored extended segment address record %d\n", Record_Nb);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ while (!feof (Filin));
+
+ rewind(Filin);
+ Segment = 0;
+ Upper_Address = 0;
+ Record_Nb = 0;
+
+ if (Starting_Address_Setted == true)
+ {
+ Records_Start = Lowest_Address;
+ Lowest_Address = Starting_Address;
+ }
+ else
+ {
+ Records_Start = Lowest_Address;
+ Starting_Address = Lowest_Address;
+ }
+
+ if (Max_Length_Setted == false)
+ Max_Length = Highest_Address - Lowest_Address + 1;
+ else
+ Highest_Address = Lowest_Address + Max_Length - 1;
+
+ /* Now, that we know the buffer size, we can allocate it. */
+ /* allocate a buffer */
+ Memory_Block = (byte *) NoFailMalloc(Max_Length);
+
+ /* For EPROM or FLASH memory types, fill unused bytes with FF or the value specified by the p option */
+ memset (Memory_Block,Pad_Byte,Max_Length);
+
+ /* Read the file & process the lines. */
+ do /* repeat until EOF(Filin) */
+ {
+ unsigned int i;
+
+ /* Read a line from input file. */
+ GetLine(Line,Filin);
+ Record_Nb++;
+
+ /* Remove carriage return/line feed at the end of line. */
+ i = strlen(Line);
+
+ //fprintf(stderr,"Record: %d; length: %d\n", Record_Nb, i);
+
+ if (--i != 0)
+ {
+ if (Line[i] == '\n') Line[i] = '\0';
+
+ /* Scan the first two bytes and nb of bytes.
+ The two bytes are read in First_Word since its use depend on the
+ record type: if it's an extended address record or a data record.
+ */
+ result = sscanf (Line, ":%2x%4x%2x%s",&Nb_Bytes,&First_Word,&Type,Data_Str);
+ if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+
+ Checksum = Nb_Bytes + (First_Word >> 8) + (First_Word & 0xFF) + Type;
+
+ p = (char *) Data_Str;
+
+ /* If we're reading the last record, ignore it. */
+ switch (Type)
+ {
+ /* Data record */
+ case 0:
+ if (Nb_Bytes == 0)
+ {
+ fprintf(stderr,"0 byte length Data record ignored\n");
+ break;
+ }
+
+ Address = First_Word;
+
+ if (Seg_Lin_Select == SEGMENTED_ADDRESS)
+ Phys_Addr = (Segment << 4) + Address;
+ else
+ /* LINEAR_ADDRESS or NO_ADDRESS_TYPE_SELECTED
+ Upper_Address = 0 as specified in the Intel spec. until an extended address
+ record is read. */
+ Phys_Addr = ((Upper_Address << 16) + Address);
+
+ /* Check that the physical address stays in the buffer's range. */
+ if ((Phys_Addr >= Lowest_Address) && (Phys_Addr <= Highest_Address))
+ {
+ /* The memory block begins at Lowest_Address */
+ Phys_Addr -= Lowest_Address;
+
+ /* Read the Data bytes. */
+ /* Bytes are written in the Memory block even if checksum is wrong. */
+ i = Nb_Bytes;
+
+ do
+ {
+ result = sscanf (p, "%2x",&temp2);
+ if (result != 1) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+ p += 2;
+
+ /* Check that the physical address stays in the buffer's range. */
+ if (Phys_Addr < Max_Length)
+ {
+ /* Overlapping record will erase the pad bytes */
+ if (Swap_Wordwise)
+ {
+ if (Memory_Block[Phys_Addr ^ 1] != Pad_Byte) fprintf(stderr,"Overlapped record detected\n");
+ Memory_Block[Phys_Addr++ ^ 1] = temp2;
+ }
+ else
+ {
+ if (Memory_Block[Phys_Addr] != Pad_Byte) fprintf(stderr,"Overlapped record detected\n");
+ Memory_Block[Phys_Addr++] = temp2;
+ }
+
+ Checksum = (Checksum + temp2) & 0xFF;
+ }
+ }
+ while (--i != 0);
+
+ /* Read the Checksum value. */
+ result = sscanf (p, "%2x",&temp2);
+ if (result != 1) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+
+ /* Verify Checksum value. */
+ Checksum = (Checksum + temp2) & 0xFF;
+ VerifyChecksumValue();
+ }
+ else
+ {
+ if (Seg_Lin_Select == SEGMENTED_ADDRESS)
+ fprintf(stderr,"Data record skipped at %4X:%4X\n",Segment,Address);
+ else
+ fprintf(stderr,"Data record skipped at %8X\n",Phys_Addr);
+ }
+
+ break;
+
+ /* End of file record */
+ case 1:
+ /* Simply ignore checksum errors in this line. */
+ break;
+
+ /* Extended segment address record */
+ case 2:
+ /* First_Word contains the offset. It's supposed to be 0000 so
+ we ignore it. */
+
+ /* First extended segment address record ? */
+ if (Seg_Lin_Select == NO_ADDRESS_TYPE_SELECTED)
+ Seg_Lin_Select = SEGMENTED_ADDRESS;
+
+ /* Then ignore subsequent extended linear address records */
+ if (Seg_Lin_Select == SEGMENTED_ADDRESS)
+ {
+ result = sscanf (p, "%4x%2x",&Segment,&temp2);
+ if (result != 2) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+
+ /* Update the current address. */
+ Phys_Addr = (Segment << 4);
+
+ /* Verify Checksum value. */
+ Checksum = (Checksum + (Segment >> 8) + (Segment & 0xFF) + temp2) & 0xFF;
+ VerifyChecksumValue();
+ }
+ break;
+
+ /* Start segment address record */
+ case 3:
+ /* Nothing to be done since it's for specifying the starting address for
+ execution of the binary code */
+ break;
+
+ /* Extended linear address record */
+ case 4:
+ /* First_Word contains the offset. It's supposed to be 0000 so
+ we ignore it. */
+
+ /* First extended linear address record ? */
+ if (Seg_Lin_Select == NO_ADDRESS_TYPE_SELECTED)
+ Seg_Lin_Select = LINEAR_ADDRESS;
+
+ /* Then ignore subsequent extended segment address records */
+ if (Seg_Lin_Select == LINEAR_ADDRESS)
+ {
+ result = sscanf (p, "%4x%2x",&Upper_Address,&temp2);
+ if (result != 2) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+
+ /* Update the current address. */
+ Phys_Addr = (Upper_Address << 16);
+
+ /* Verify Checksum value. */
+ Checksum = (Checksum + (Upper_Address >> 8) + (Upper_Address & 0xFF) + temp2)
+ & 0xFF;
+ VerifyChecksumValue();
+ }
+ break;
+
+ /* Start linear address record */
+ case 5:
+ /* Nothing to be done since it's for specifying the starting address for
+ execution of the binary code */
+ break;
+ default:
+ fprintf(stderr,"Unknown record type\n");
+ break;
+ }
+ }
+ }
+ while (!feof (Filin));
+ /*-----------------------------------------------------------------------------*/
+
+ fprintf(stdout,"Binary file start = %08X\n",Lowest_Address);
+ fprintf(stdout,"Records start = %08X\n",Records_Start);
+ fprintf(stdout,"Highest address = %08X\n",Highest_Address);
+ fprintf(stdout,"Pad Byte = %X\n", Pad_Byte);
+
+ WriteMemory();
+
+#ifdef USE_FILE_BUFFERS
+ free (FilinBuf);
+ free (FiloutBuf);
+#endif
+
+ fclose (Filin);
+ fclose (Filout);
+
+ if (Status_Checksum_Error && Enable_Checksum_Error)
+ {
+ fprintf(stderr,"Checksum error detected.\n");
+ return 1;
+ }
+
+ if (!Fileread)
+ usage();
+ return 0;
+}
diff --git a/tools/hex2bin-2.0/src/hex2bin.pod b/tools/hex2bin-2.0/src/hex2bin.pod
new file mode 100644
index 0000000..a8eb238
--- /dev/null
+++ b/tools/hex2bin-2.0/src/hex2bin.pod
@@ -0,0 +1,161 @@
+HEX2BIN 1 "2015 february 22nd" "Hex2bin Version 2.0"
+=head1 NAME
+
+hex2bin/mot2bin \- converts Intel/Motorola hex files into binary
+
+=head1 SYNOPSIS
+
+hex2bin [options] file
+
+Option list:
+ [-c]
+ [-C Poly Init RefIn RefOut XorOut]
+ [-e extension]
+ [-E 0|1]
+ [-f address]
+ [-F address value]
+ [-k checksum type]
+ [-l length]
+ [-m minimum block size]
+ [-p pad byte]
+ [-r start end]
+ [-s address]
+ [-w]
+
+=head1 DESCRIPTION
+
+B<Hex2bin>
+is a program that converts an Intel hex format into binary.
+It can handle the extended Intel hex format. Both the segmented
+and linear address records are supported.
+Records need not be sorted and there can be gaps between records.
+Records are assumed to be non-overlapping.
+Padding bytes may be specified and a checksum may be inserted in the
+binary file.
+
+B<Mot2bin>
+does the same with Motorola hex files. It has the same features and command line
+options. 24 bit and 32 bit records are supported.
+
+=head1 OPTIONS
+
+Options can be specified in any order, with the file name at the end. Options are
+now case sensitive. All option values are specified in hexadecimal.
+
+B<-c>
+
+Enables checksum verification.
+
+By default, it ignores checksum errors in the hex file, so that someone can change
+by hand some bytes with a text editor, allowing quick fixes without recompiling a source
+code all over again. This is useful when tweaking constants directly in the code or
+something similar. If you want checksum error reporting, specify the option -c.
+
+B<Ex.: hex2bin -c example.hex>
+
+If there is a checksum error somewhere, the program will continue the
+conversion anyway.
+
+B<-C Poly Init RefIn RefOut XorOut>
+
+CRC parameters. See the doc/CRC list.txt file for a description of common CRCs. See also
+the test/Makefile for examples of command lines. Needs B<-k> and B<-f> option.
+RefIn and RefOut parameters are specified by B<t> or B<f> for true or false.
+
+B<-d>
+
+Display the list of available check methods and sizes.
+
+B<-e extension>
+
+By default, the output file will have an extension B<filename.bin>.
+Another extension may be specified with this command:
+
+B<Ex.: hex2bin -e com example.hex>
+
+The output file will be example.com
+
+B<-E 0|1>
+
+Endianness for writing the check result or forcing a 16-bit value.
+ B<0>: little, B<1>: big.
+
+By default, little endian is used.
+
+B<-f address>
+
+Address in hexadecimal for inserting the check value in the binary file. Needs B<-k>
+option to specify the check method. A range can be specified with option B<-r>.
+
+B<-F address value>
+
+Address and value of checksum to insert (force) in the binary file. Needs B<-k>
+option to specify the size. The value is written as is.
+
+B<-k 0-4>
+
+In many cases, someone needs to insert a check value in the binary file. For example,
+a boot rom is programmed with a checksum which is verified at power-up. This feature
+uses also options B<-r>, B<-C> and B<-f>. Display the list with B<-d>.
+
+Select the checksum type to insert into the binary file
+ 0: Checksum 8-bit
+ 1: Checksum 16-bit
+ 2: CRC8
+ 3: CRC16
+ 4: CRC32
+
+B<-l length>
+
+The binary file will be padded with FF or pad bytes as specified by the option
+below, up to a maximal Length (Starting address + Length -1 is Max Address)
+
+B<-m minimum_block_size>
+
+File Size Dimension will be a multiple of Minimum block size.
+File will be filled with Pattern.
+Length must be a power of 2 in hexadecimal [see B<-l> option]
+Attention this option is STRONGER than Maximal Length
+
+B<-p pad_byte>
+
+Pads unused locations with the specified byte.
+
+By default, this byte is FF, which is the unprogrammed value for most EPROM/EEPROM/Flash.
+
+B<Ex.: hex2bin -p 3E example.hex>
+
+B<-r [start] [end]>
+
+Range to compute binary checksum over (default is min and max addresses)
+
+B<-s address>
+
+Specify the starting address of the binary file.
+
+Normally, hex2bin will generate a binary file starting at the lowest address in
+the hex file. If the lowest address isn't 0000, ex: 0100, the first byte that
+should be at 0100 will be stored at address 0000 in the binary file. This may
+cause problems when using the binary file to program an EPROM.
+
+If you can't specify the starting address (or offset) to your EPROM programmer,
+you can specify a starting address on the command line:
+
+B<Ex.: hex2bin -s 0000 records_start_at_0100.hex>
+
+The bytes will be stored in the binary file with a padding from 0000 to the
+lowest address (00FF in this case). Padding bytes are all FF by default so an EPROM
+programmer can skip these bytes when programming. The padding value can be changed
+with the -p option.
+
+B<-w>
+
+Swap wordwise (low <-> high). Used by Microchip's MPLAB IDE
+
+=head1 NOTES
+
+This program does minimal error checking since many hex files are
+generated by known good assemblers.
+
+=head1 AUTHOR
+Jacques Pelletier (jpelletier@ieee.org) - version 2.0
diff --git a/tools/hex2bin-2.0/src/libcrc.c b/tools/hex2bin-2.0/src/libcrc.c
new file mode 100644
index 0000000..02bcd4b
--- /dev/null
+++ b/tools/hex2bin-2.0/src/libcrc.c
@@ -0,0 +1,204 @@
+/*********************************************************************
+ * *
+ * Library : lib_crc *
+ * File : lib_crc.c *
+ * Author : Lammert Bies 1999-2008 *
+ * E-mail : info@lammertbies.nl *
+ * Language : ANSI C *
+ * *
+ * *
+ * Description *
+ * =========== *
+ * *
+ * The file lib_crc.c contains the private and public func- *
+ * tions used for the calculation of CRC-16, CRC-CCITT and *
+ * CRC-32 cyclic redundancy values. *
+ * *
+ * *
+ * Dependencies *
+ * ============ *
+ * *
+ * libcrc.h CRC definitions and prototypes *
+ * *
+ ********************************************************************/
+#include <stdint.h>
+
+#ifndef G_GUINT64_CONSTANT
+#define G_GUINT64_CONSTANT(val) (val##UL)
+#endif
+
+void *crc_table;
+
+/* private */
+
+void init_crc8_normal_tab(uint8_t polynom)
+{
+ int i, j;
+ uint8_t crc;
+ uint8_t *p;
+
+ p = (uint8_t *) crc_table;
+
+ for (i=0; i<256; i++)
+ {
+ crc = (uint8_t) i;
+
+ for (j=0; j<8; j++)
+ {
+ if (crc & 0x80) crc = (crc << 1) ^ polynom;
+ else crc <<= 1;
+ }
+ *p++ = crc;
+ }
+}
+
+void init_crc8_reflected_tab(uint8_t polynom)
+{
+ int i, j;
+ uint8_t crc;
+ uint8_t *p;
+
+ p = (uint8_t *) crc_table;
+
+ for (i=0; i<256; i++)
+ {
+ crc = (uint8_t) i;
+
+ for (j=0; j<8; j++)
+ {
+ if (crc & 0x01) crc = (crc >> 1) ^ polynom;
+ else crc >>= 1;
+ }
+ *p++ = crc;
+ }
+}
+
+/* Common routines for calculations */
+void init_crc16_normal_tab(uint16_t polynom)
+{
+ int i, j;
+ uint16_t crc;
+ uint16_t *p;
+
+ p = (uint16_t *) crc_table;
+
+ for (i=0; i<256; i++)
+ {
+ crc = ((uint16_t) i) << 8;
+
+ for (j=0; j<8; j++)
+ {
+ if ( crc & 0x8000 ) crc = ( crc << 1 ) ^ polynom;
+ else crc <<= 1;
+ }
+ *p++ = crc;
+ }
+}
+
+void init_crc16_reflected_tab(uint16_t polynom)
+{
+ int i, j;
+ uint16_t crc;
+ uint16_t *p;
+
+ p = (uint16_t *) crc_table;
+
+ for (i=0; i<256; i++)
+ {
+ crc = (uint16_t) i;
+
+ for (j=0; j<8; j++)
+ {
+ if ( crc & 0x0001 ) crc = ( crc >> 1 ) ^ polynom;
+ else crc >>= 1;
+ }
+ *p++ = crc;
+ }
+}
+
+void init_crc32_normal_tab(uint32_t polynom)
+{
+ int i, j;
+ uint32_t crc;
+ uint32_t *p;
+
+ p = (uint32_t *) crc_table;
+
+ for (i=0; i<256; i++)
+ {
+ crc = ((uint32_t) i) << 24;
+
+ for (j=0; j<8; j++)
+ {
+ if ( crc & 0x80000000L ) crc = ( crc << 1 ) ^ polynom;
+ else crc <<= 1;
+ }
+ *p++ = crc;
+ }
+}
+
+void init_crc32_reflected_tab(uint32_t polynom)
+{
+ int i, j;
+ uint32_t crc;
+ uint32_t *p;
+
+ p = (uint32_t *) crc_table;
+
+ for (i=0; i<256; i++)
+ {
+ crc = (uint32_t) i;
+
+ for (j=0; j<8; j++)
+ {
+ if ( crc & 0x00000001L ) crc = ( crc >> 1 ) ^ polynom;
+ else crc >>= 1;
+ }
+ *p++ = crc;
+ }
+}
+
+/* Common routines for calculations */
+
+uint8_t update_crc8(uint8_t crc, uint8_t c)
+{
+ return (((uint8_t *) crc_table)[crc ^ c]);
+}
+
+uint16_t update_crc16_normal(uint16_t crc, char c )
+{
+ uint16_t short_c;
+
+ short_c = 0x00ff & (uint16_t) c;
+
+ /* Normal form */
+ return (crc << 8) ^ ((uint16_t *) crc_table)[(crc >> 8) ^ short_c];
+}
+
+uint16_t update_crc16_reflected(uint16_t crc, char c )
+{
+ uint16_t short_c;
+
+ short_c = 0x00ff & (uint16_t) c;
+
+ /* Reflected form */
+ return (crc >> 8) ^ ((uint16_t *) crc_table)[(crc ^ short_c) & 0xff];
+}
+
+uint32_t update_crc32_normal(uint32_t crc, char c )
+{
+ uint32_t long_c;
+
+ long_c = 0x000000ffL & (uint32_t) c;
+
+ return (crc << 8) ^ ((uint32_t *) crc_table)[((crc >> 24) ^ long_c) & 0xff];
+}
+
+uint32_t update_crc32_reflected(uint32_t crc, char c )
+{
+ uint32_t long_c;
+
+ long_c = 0x000000ffL & (uint32_t) c;
+
+ return (crc >> 8) ^ ((uint32_t *) crc_table)[(crc ^ long_c) & 0xff];
+}
diff --git a/tools/hex2bin-2.0/src/libcrc.h b/tools/hex2bin-2.0/src/libcrc.h
new file mode 100644
index 0000000..39f20d0
--- /dev/null
+++ b/tools/hex2bin-2.0/src/libcrc.h
@@ -0,0 +1,44 @@
+ /********************************************************************
+ * *
+ * Library : lib_crc *
+ * File : lib_crc.h *
+ * Author : Lammert Bies 1999-2008 *
+ * E-mail : info@lammertbies.nl *
+ * Language : ANSI C *
+ * *
+ * *
+ * Description *
+ * =========== *
+ * *
+ * The file lib_crc.h contains public definitions and proto- *
+ * types for the CRC functions present in lib_crc.c. *
+ * *
+ * *
+ * Dependencies *
+ * ============ *
+ * *
+ * none *
+ * *
+ * *
+ ********************************************************************/
+#ifndef _LIBCRC_H_
+#define _LIBCRC_H_
+
+void *crc_table;
+
+void init_crc8_normal_tab(uint8_t polynom);
+void init_crc8_reflected_tab(uint8_t polynom);
+
+void init_crc16_normal_tab(uint16_t polynom);
+void init_crc16_reflected_tab(uint16_t polynom);
+void init_crc32_normal_tab(uint32_t polynom);
+void init_crc32_reflected_tab(uint32_t polynom);
+
+uint8_t update_crc8(uint8_t crc, uint8_t c);
+
+uint16_t update_crc16_normal(uint16_t crc, char c );
+uint16_t update_crc16_reflected(uint16_t crc, char c );
+uint32_t update_crc32_normal(uint32_t crc, char c );
+uint32_t update_crc32_reflected(uint32_t crc, char c );
+
+#endif /* _LIBCRC_H_ */
diff --git a/tools/hex2bin-2.0/src/mot2bin.c b/tools/hex2bin-2.0/src/mot2bin.c
new file mode 100644
index 0000000..9e0e23c
--- /dev/null
+++ b/tools/hex2bin-2.0/src/mot2bin.c
@@ -0,0 +1,518 @@
+/*
+mot2bin converts a Motorola hex file to binary.
+
+Copyright (C) 2015, Jacques Pelletier
+checksum extensions Copyright (C) 2004 Rockwell Automation
+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.
+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.
+
+20040617 Alf Lacis: Added pad byte (may not always want FF).
+ Added initialisation to Checksum to remove GNU
+ compiler warning about possible uninitialised usage
+ Added 2x'break;' to remove GNU compiler warning about label at
+ end of compound statement
+ Added PROGRAM & VERSION strings.
+
+20071005 PG: Improvements on options parsing
+20091212 JP: Corrected crash on 0 byte length data records
+20100402 JP: ADDRESS_MASK is now calculated from MEMORY_SIZE
+
+20120125 Danny Schneider:
+ Added code for filling a binary file to a given Max_Length relative to
+ Starting Address if Max-Address is larger than Highest-Address
+20120509 Yoshimasa Nakane:
+ modified error checking (also for output file, JP)
+20141005 JP: added support for byte swapped hex files
+ corrected bug caused by extra LF at end or within file
+20141121 Slucx: added line for removing extra CR when entering file name at run time.
+20150116 Richard Genoud (Paratronic): correct buffer overflows/wrong results with the -l flag
+20150122 JP: added support for different check methods
+20150221 JP: rewrite of the checksum write/force value
+*/
+
+#define PROGRAM "mot2bin"
+#define VERSION "2.0"
+
+#include "common.h"
+
+const char *Pgm_Name = PROGRAM;
+
+int main (int argc, char *argv[])
+{
+ /* line inputted from file */
+ char Line[MAX_LINE_SIZE];
+
+ /* flag that a file was read */
+ bool Fileread;
+
+ /* cmd-line parameter # */
+ char *p;
+
+ int Param, result;
+
+ /* Application specific */
+
+ unsigned int Nb_Bytes;
+ unsigned int First_Word, Address;
+
+ unsigned int Phys_Addr, Type;
+ unsigned int Exec_Address;
+ unsigned int temp;
+ unsigned int Record_Count, Record_Checksum;
+ unsigned int Records_Start; // Lowest address of the records
+
+ unsigned int temp2;
+
+ byte Data_Str[MAX_LINE_SIZE];
+
+ fprintf (stdout,PROGRAM" v"VERSION", Copyright (C) 2015 Jacques Pelletier & contributors\n\n");
+
+ if (argc == 1)
+ usage();
+
+ strcpy(Extension, "bin"); /* default is for binary file extension */
+
+ /* read file */
+ Starting_Address = 0;
+
+ /*
+ use p for parsing arguments
+ use i for number of parameters to skip
+ use c for the current option
+ */
+ for (Param = 1; Param < argc; Param++)
+ {
+ int i = 0;
+ char c;
+
+ p = argv[Param];
+ c = *(p+1); /* Get option character */
+
+ if ( _IS_OPTION_(*p) )
+ {
+ // test for no space between option and parameter
+ if (strlen(p) != 2) usage();
+
+ switch(c)
+ {
+ /* file extension */
+ case 'c':
+ Enable_Checksum_Error = true;
+ i = 0;
+ break;
+ case 'd':
+ DisplayCheckMethods();
+ case 'e':
+ GetExtension(argv[Param + 1],Extension);
+ i = 1; /* add 1 to Param */
+ break;
+ case 'f':
+ Cks_Addr = GetHex(argv[Param + 1]);
+ Cks_Addr_set = true;
+ i = 1; /* add 1 to Param */
+ break;
+ case 'F':
+ Cks_Addr = GetHex(argv[Param + 1]);
+ Cks_Value = GetHex(argv[Param + 2]);
+ Force_Value = true;
+ i = 2; /* add 2 to Param */
+ break;
+ case 'k':
+ Cks_Type = GetHex(argv[Param + 1]);
+ {
+ if (Cks_Type > LAST_CHECK_METHOD) usage();
+ }
+ i = 1; /* add 1 to Param */
+ break;
+ case 'l':
+ Max_Length = GetHex(argv[Param + 1]);
+ Max_Length_Setted = true;
+ i = 1; /* add 1 to Param */
+ break;
+ case 'm':
+ Minimum_Block_Size = GetHex(argv[Param + 1]);
+ Minimum_Block_Size_Setted = true;
+ i = 1; /* add 1 to Param */
+ break;
+ case 'p':
+ Pad_Byte = GetHex(argv[Param + 1]);
+ i = 1; /* add 1 to Param */
+ break;
+ case 'r':
+ Cks_Start = GetHex(argv[Param + 1]);
+ Cks_End = GetHex(argv[Param + 2]);
+ Cks_range_set = true;
+ i = 2; /* add 2 to Param */
+ break;
+ case 's':
+ Starting_Address = GetHex(argv[Param + 1]);
+ Starting_Address_Setted = true;
+ i = 1; /* add 1 to Param */
+ break;
+ case 'w':
+ Swap_Wordwise = true;
+ i = 0;
+ break;
+ case 'C':
+ Crc_Poly = GetHex(argv[Param + 1]);
+ Crc_Init = GetHex(argv[Param + 2]);
+ Crc_RefIn = GetBoolean(argv[Param + 3]);
+ Crc_RefOut = GetBoolean(argv[Param + 4]);
+ Crc_XorOut = GetHex(argv[Param + 5]);
+ CrcParamsCheck();
+ i = 5; /* add 5 to Param */
+ break;
+
+ case '?':
+ case 'h':
+ default:
+ usage();
+ } /* switch */
+
+ /* Last parameter is not a filename */
+ if (Param == argc-1) usage();
+
+ // fprintf(stderr,"Param: %d, option: %c\n",Param,c);
+
+ /* if (Param + i) < (argc -1) */
+ if (Param < argc -1 -i) Param += i;
+ else usage();
+
+ }
+ else
+ break;
+ /* if option */
+ } /* for Param */
+
+ /* when user enters input file name */
+
+ /* Assume last parameter is filename */
+ strcpy(Filename,argv[argc -1]);
+
+ /* Just a normal file name */
+ NoFailOpenInputFile (Filename);
+ PutExtension(Filename, Extension);
+ NoFailOpenOutputFile(Filename);
+ Fileread = true;
+
+ /* To begin, assume the lowest address is at the end of the memory.
+ While reading each records, subsequent addresses will lower this number.
+ At the end of the input file, this value will be the lowest address.
+
+ A similar assumption is made for highest address. It starts at the
+ beginning of memory. While reading each records, subsequent addresses will raise this number.
+ At the end of the input file, this value will be the highest address. */
+ Lowest_Address = (unsigned int)-1;
+ Highest_Address = 0;
+ Records_Start = 0;
+ Record_Nb = 0;
+
+ /* get highest and lowest addresses so that we can allocate the right size */
+ do
+ {
+ unsigned int i;
+
+ /* Read a line from input file. */
+ GetLine(Line,Filin);
+ Record_Nb++;
+
+ /* Remove carriage return/line feed at the end of line. */
+ i = strlen(Line);
+
+ if (--i != 0)
+ {
+ if (Line[i] == '\n') Line[i] = '\0';
+
+ p = (char *) Data_Str;
+
+ switch(Line[1])
+ {
+ case '0':
+ break;
+
+ /* 16 bits address */
+ case '1':
+ result = sscanf (Line,"S%1x%2x%4x",&Type,&Nb_Bytes,&First_Word);
+ if (result != 3) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+
+ /* Adjust Nb_Bytes for the number of data bytes */
+ Nb_Bytes = Nb_Bytes - 3;
+ break;
+
+ /* 24 bits address */
+ case '2':
+ result = sscanf (Line,"S%1x%2x%6x",&Type,&Nb_Bytes,&First_Word);
+ if (result != 3) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+
+ /* Adjust Nb_Bytes for the number of data bytes */
+ Nb_Bytes = Nb_Bytes - 4;
+ break;
+
+ /* 32 bits address */
+ case '3':
+ result = sscanf (Line,"S%1x%2x%8x",&Type,&Nb_Bytes,&First_Word);
+ if (result != 3) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+
+ /* Adjust Nb_Bytes for the number of data bytes */
+ Nb_Bytes = Nb_Bytes - 5;
+ break;
+ }
+
+ Phys_Addr = First_Word;
+
+ /* Set the lowest address as base pointer. */
+ if (Phys_Addr < Lowest_Address)
+ Lowest_Address = Phys_Addr;
+
+ /* Same for the top address. */
+ temp = Phys_Addr + Nb_Bytes -1;
+
+ if (temp > Highest_Address)
+ Highest_Address = temp;
+ }
+ }
+ while (!feof (Filin));
+
+ if (Starting_Address_Setted == true)
+ {
+ Records_Start = Lowest_Address;
+ Lowest_Address = Starting_Address;
+ }
+ else
+ {
+ Records_Start = Lowest_Address;
+ Starting_Address = Lowest_Address;
+ }
+
+ if (Max_Length_Setted == false)
+ Max_Length = Highest_Address - Lowest_Address + 1;
+ else
+ Highest_Address = Lowest_Address + Max_Length - 1;
+
+ /* Now, that we know the buffer size, we can allocate it. */
+ /* allocate a buffer */
+ Memory_Block = (byte *) NoFailMalloc(Max_Length);
+
+ /* For EPROM or FLASH memory types, fill unused bytes with FF or the value specified by the p option */
+ memset (Memory_Block,Pad_Byte,Max_Length);
+
+ rewind(Filin);
+ Record_Nb = 0;
+
+ /* Read the file & process the lines. */
+ do /* repeat until EOF(Filin) */
+ {
+ int i;
+
+ Checksum = 0;
+
+ /* Read a line from input file. */
+ GetLine(Line,Filin);
+ Record_Nb++;
+
+ /* Remove carriage return/line feed at the end of line. */
+ i = strlen(Line);
+
+ if (--i != 0)
+ {
+ if (Line[i] == '\n') Line[i] = '\0';
+
+ /* Scan starting address and nb of bytes. */
+ /* Look at the record type after the 'S' */
+ Type = 0;
+
+ switch(Line[1])
+ {
+ case '0':
+ result = sscanf (Line,"S0%2x0000484452%2x",&Nb_Bytes,&Record_Checksum);
+ if (result != 2) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+ Checksum = Nb_Bytes + 0x48 + 0x44 + 0x52;
+
+ /* Adjust Nb_Bytes for the number of data bytes */
+ Nb_Bytes = 0;
+ break;
+
+ /* 16 bits address */
+ case '1':
+ result = sscanf (Line,"S%1x%2x%4x%s",&Type,&Nb_Bytes,&Address,Data_Str);
+ if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+ Checksum = Nb_Bytes + (Address >> 8) + (Address & 0xFF);
+
+ /* Adjust Nb_Bytes for the number of data bytes */
+ Nb_Bytes = Nb_Bytes - 3;
+ break;
+
+ /* 24 bits address */
+ case '2':
+ result = sscanf (Line,"S%1x%2x%6x%s",&Type,&Nb_Bytes,&Address,Data_Str);
+ if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+ Checksum = Nb_Bytes + (Address >> 16) + (Address >> 8) + (Address & 0xFF);
+
+ /* Adjust Nb_Bytes for the number of data bytes */
+ Nb_Bytes = Nb_Bytes - 4;
+ break;
+
+ /* 32 bits address */
+ case '3':
+ result = sscanf (Line,"S%1x%2x%8x%s",&Type,&Nb_Bytes,&Address,Data_Str);
+ if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+ Checksum = Nb_Bytes + (Address >> 24) + (Address >> 16) + (Address >> 8) + (Address & 0xFF);
+
+ /* Adjust Nb_Bytes for the number of data bytes */
+ Nb_Bytes = Nb_Bytes - 5;
+ break;
+
+ case '5':
+ result = sscanf (Line,"S%1x%2x%4x%2x",&Type,&Nb_Bytes,&Record_Count,&Record_Checksum);
+ if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+ Checksum = Nb_Bytes + (Record_Count >> 8) + (Record_Count & 0xFF);
+
+ /* Adjust Nb_Bytes for the number of data bytes */
+ Nb_Bytes = 0;
+ break;
+
+ case '7':
+ result = sscanf (Line,"S%1x%2x%8x%2x",&Type,&Nb_Bytes,&Exec_Address,&Record_Checksum);
+ if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+ Checksum = Nb_Bytes + (Exec_Address >> 24) + (Exec_Address >> 16) + (Exec_Address >> 8) + (Exec_Address & 0xFF);
+ Nb_Bytes = 0;
+ break;
+
+ case '8':
+ result = sscanf (Line,"S%1x%2x%6x%2x",&Type,&Nb_Bytes,&Exec_Address,&Record_Checksum);
+ if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+ Checksum = Nb_Bytes + (Exec_Address >> 16) + (Exec_Address >> 8) + (Exec_Address & 0xFF);
+ Nb_Bytes = 0;
+ break;
+ case '9':
+ result = sscanf (Line,"S%1x%2x%4x%2x",&Type,&Nb_Bytes,&Exec_Address,&Record_Checksum);
+ if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+ Checksum = Nb_Bytes + (Exec_Address >> 8) + (Exec_Address & 0xFF);
+ Nb_Bytes = 0;
+ break;
+ }
+
+ p = (char *) Data_Str;
+
+ /* If we're reading the last record, ignore it. */
+ switch (Type)
+ {
+ /* Data record */
+ case 1:
+ case 2:
+ case 3:
+ if (Nb_Bytes == 0)
+ {
+ fprintf(stderr,"0 byte length Data record ignored\n");
+ break;
+ }
+
+ Phys_Addr = Address;
+
+ /* Read the Data bytes. */
+ i = Nb_Bytes;
+
+ do
+ {
+ result = sscanf (p, "%2x",&temp2);
+ if (result != 1) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+ p += 2;
+
+ /* Overlapping record will erase the pad bytes */
+ if (Swap_Wordwise)
+ {
+ if (Memory_Block[Phys_Addr ^ 1] != Pad_Byte) fprintf(stderr,"Overlapped record detected\n");
+ Memory_Block[Phys_Addr++ ^ 1] = temp2;
+ }
+ else
+ {
+ if (Memory_Block[Phys_Addr] != Pad_Byte) fprintf(stderr,"Overlapped record detected\n");
+ Memory_Block[Phys_Addr++] = temp2;
+ }
+
+ Checksum = (Checksum + temp2) & 0xFF;
+ }
+ while (--i != 0);
+
+ /* Read the Checksum value. */
+ result = sscanf (p, "%2x",&Record_Checksum);
+ if (result != 1) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb);
+ break;
+
+ case 5:
+ fprintf(stderr,"Record total: %d\n",Record_Count);
+ break;
+
+ case 7:
+ fprintf(stderr,"Execution Address (unused): %08X\n",Exec_Address);
+ break;
+
+ case 8:
+ fprintf(stderr,"Execution Address (unused): %06X\n",Exec_Address);
+ break;
+
+ case 9:
+ fprintf(stderr,"Execution Address (unused): %04X\n",Exec_Address);
+ break;
+
+ /* Ignore all other records */
+ default:;
+ }
+
+ Record_Checksum &= 0xFF;
+
+ /* Verify Checksum value. */
+ if (((Record_Checksum + Checksum) != 0xFF) && Enable_Checksum_Error)
+ {
+ fprintf(stderr,"Checksum error in record %d: should be %02X\n",Record_Nb, 255-Checksum);
+ Status_Checksum_Error = true;
+ }
+ }
+ }
+ while (!feof (Filin));
+ /*-----------------------------------------------------------------------------*/
+
+ fprintf(stdout,"Binary file start = %08X\n",Lowest_Address);
+ fprintf(stdout,"Records start = %08X\n",Records_Start);
+ fprintf(stdout,"Highest address = %08X\n",Highest_Address);
+ fprintf(stdout,"Pad Byte = %X\n", Pad_Byte);
+
+ WriteMemory();
+
+#ifdef USE_FILE_BUFFERS
+ free (FilinBuf);
+ free (FiloutBuf);
+#endif
+
+ fclose (Filin);
+ fclose (Filout);
+
+ if (Status_Checksum_Error && Enable_Checksum_Error)
+ {
+ fprintf(stderr,"Checksum error detected.\n");
+ return 1;
+ }
+
+ if (!Fileread)
+ usage();
+ return 0;
+}
diff --git a/tools/map.py b/tools/map.py
new file mode 100755
index 0000000..5c6e27e
--- /dev/null
+++ b/tools/map.py
@@ -0,0 +1,385 @@
+#!/usr/bin/env python3
+
+from argparse import ArgumentParser
+from collections import defaultdict
+from os import path
+import json
+import os
+import subprocess
+import struct
+import sys
+import tempfile
+import traceback
+
+__version__ = "1.0"
+
+DEF_ROOM_WIDTH = 32
+DEF_ROOM_HEIGHT = 24
+DEF_BITS = 8
+
+DEF_MAP_CONF = "map_conf.json"
+
+"""
+Format:
+
+ 2 bytes: map data length (0 for empty map; no more data included)
+ 1 byte: entities length (1 is just the terminator 0xff)
+ map length bytes: map data (n-bit per tile) H x W x n (may be compressed)
+ i bytes: entity data (0xff for end)
+
+Expected layers: Map and Entities
+"""
+
+
+def apultra_compress(data):
+ with tempfile.NamedTemporaryFile() as fd:
+ fd.write(bytearray(data))
+ fd.flush()
+
+ ap_name = fd.name + ".ap"
+ subprocess.call(["apultra", "-v", fd.name, ap_name], stdout=sys.stderr)
+
+ with open(ap_name, "rb") as fd:
+ out = fd.read()
+ os.unlink(ap_name)
+
+ return [int(byte) for byte in out]
+
+
+def find_name(data, name):
+ for item in data:
+ if item.get("name").lower() == name.lower():
+ return item
+ raise ValueError("%r not found" % name)
+
+
+def find_id(data, id):
+ for item in data:
+ if item.get("id") == id:
+ return item
+ raise ValueError("id %r not found", id)
+
+
+def get_property(obj, name, default):
+ props = obj.get("properties", {})
+
+ if isinstance(props, dict):
+ return props.get(name, default)
+
+ for p in props:
+ if p["name"] == name:
+ return p["value"]
+
+ return default
+
+
+def main():
+
+ parser = ArgumentParser(description="Map importer",
+ epilog="Copyright (C) 2020 Juan J Martinez <jjm@usebox.net>",
+ )
+
+ parser.add_argument(
+ "--version", action="version", version="%(prog)s " + __version__)
+ parser.add_argument(
+ "--room-width", dest="rw", default=DEF_ROOM_WIDTH, type=int,
+ help="room width (default: %s)" % DEF_ROOM_WIDTH)
+ parser.add_argument(
+ "--room-height", dest="rh", default=DEF_ROOM_HEIGHT, type=int,
+ help="room height (default: %s)" % DEF_ROOM_HEIGHT)
+ parser.add_argument("--max-ents", dest="max_ents", default=0, type=int,
+ help="max entities per room (default: unlimited)")
+ parser.add_argument("--max-bytes", dest="max_bytes", default=0, type=int,
+ help="max bytes per room (default: unlimited)")
+ parser.add_argument("-b", dest="bin", action="store_true",
+ help="output binary data (default: C code)")
+ parser.add_argument("-d", dest="dir", default=".", type=str,
+ help="directory to generate the bin files (default: .)")
+ parser.add_argument("-c", dest="conf", default=DEF_MAP_CONF, type=str,
+ help="JSON configuration file (default: %s)" % DEF_MAP_CONF)
+ parser.add_argument("--aplib", dest="aplib", action="store_true",
+ help="APLIB compressed")
+ parser.add_argument("-r", dest="reverse", action="store_true",
+ help="Reverse map order")
+ parser.add_argument("-q", dest="quiet", action="store_true",
+ help="Don't output stats on stderr")
+ parser.add_argument("map_json", help="Map to import")
+ parser.add_argument("id", help="variable name")
+
+ args = parser.parse_args()
+
+ with open(args.conf, "rt") as fd:
+ conf = json.load(fd)
+
+ et_names = [d["name"] for d in conf["entities"]]
+ et_weigths = dict((d["name"], d["w"]) for d in conf["entities"])
+ et_bytes = dict((d["name"], d["bytes"]) for d in conf["entities"])
+
+ with open(args.map_json, "rt") as fd:
+ data = json.load(fd)
+
+ mh = data.get("height", 0)
+ mw = data.get("width", 0)
+
+ if mh < args.rh or mh % args.rh:
+ parser.error("Map size height not multiple of the room size")
+ if mw < args.rw or mw % args.rw:
+ parser.error("Map size witdh not multiple of the room size")
+
+ tilewidth = data["tilewidth"]
+ tileheight = data["tileheight"]
+
+ tile_layer = find_name(data["layers"], "Map")["data"]
+
+ def_tileset = find_name(data["tilesets"], "default")
+ firstgid = def_tileset.get("firstgid")
+
+ out = []
+ for y in range(0, mh, args.rh):
+ for x in range(0, mw, args.rw):
+ block = []
+ for j in range(args.rh):
+ for i in range(args.rw):
+ block.append(tile_layer[x + i + (y + j) * mw] - firstgid)
+
+ # pack
+ current = []
+ for i in range(0, args.rh * args.rw, 8 // DEF_BITS):
+ tiles = []
+ for k in range(8 // DEF_BITS):
+ tiles.append(block[i + k])
+
+ b = 0
+ pos = 8
+ for k in range(8 // DEF_BITS):
+ pos -= DEF_BITS
+ b |= (tiles[k] & ((2**DEF_BITS) - 1)) << pos
+
+ current.append(b)
+
+ out.append(current)
+
+ # track empty maps
+ empty = []
+ for i, block in enumerate(out):
+ if all([byte == 0xff for byte in block]):
+ empty.append(i)
+
+ if args.aplib:
+ compressed = []
+ for i, block in enumerate(out):
+ if i in empty:
+ compressed.append(None)
+ continue
+ compressed.append(apultra_compress(block))
+ out = compressed
+
+ # add the map header
+ for i in range(len(out)):
+ if out[i] is None:
+ continue
+ size = len(out[i])
+
+ # ents size placeholder 0
+ out[i] = [size & 0xff, size >> 8, 0] + out[i]
+
+ entities_layer = find_name(data["layers"], "Entities")
+ if len(entities_layer):
+ map_ents = defaultdict(list)
+ map_ents_w = defaultdict(int)
+ map_ents_bytes = defaultdict(int)
+ map_ents_names = set()
+
+ def check_bytes(name):
+ if name not in map_ents_names:
+ # update the entity size in bytes count per map
+ try:
+ map_ents_bytes[m] += et_bytes[name]
+ map_ents_names.add(name)
+ except KeyError:
+ parser.error("max_bytes: no 'bytes' found for %r" % name)
+
+ try:
+ objs = sorted(
+ entities_layer["objects"], key=lambda o: et_names.index(o["name"].lower()))
+ except ValueError:
+ parser.error("map has an unnamed object")
+
+ for obj in objs:
+ name = obj["name"].lower()
+ m = ((obj["x"] // tilewidth) // args.rw) \
+ + (((obj["y"] // tileheight) // args.rh) * (mw // args.rw))
+ x = obj["x"] % (args.rw * tilewidth)
+ y = obj["y"] % (args.rh * tileheight)
+
+ if name == "blocked":
+ if obj["width"] > obj["height"]:
+ if y == 0:
+ # up blocked
+ out[m][1] |= (1 << 4)
+ else:
+ # down blocked
+ out[m][1] |= (1 << 5)
+ else:
+ if x == 0:
+ # left blocked
+ out[m][1] |= (1 << 6)
+ else:
+ # tight blocked
+ out[m][1] |= (1 << 7)
+ continue
+
+ t = et_names.index(name)
+
+ # MSB is direction
+
+ param = int(get_property(obj, "param", 0))
+ if param == 1:
+ t |= 128
+
+ if args.max_ents:
+ # update the entity count per map
+ try:
+ map_ents_w[m] += et_weigths[name]
+ except KeyError:
+ parser.error("max_ents: no 'w' found for %r" % name)
+
+ if args.max_bytes:
+ check_bytes(name)
+
+ special = None
+
+ # specials
+ if get_property(obj, "fixed", None) is not None:
+ if obj["width"] >= obj["height"]:
+ special = obj["width"] - tilewidth
+ if not param:
+ x += special
+ special //= tilewidth
+ # flag horizonal
+ special |= 128
+ else:
+ special = obj["height"] - tileheight
+ if not param:
+ y += special
+ special //= tileheight
+
+ if name == "elevator":
+ try:
+ respawn = json.loads(get_property(obj, "respawn", "[]"))
+ if args.max_bytes:
+ for name in respawn:
+ check_bytes(name)
+ special = [et_names.index(et) for et in respawn]
+ assert(len(special) < 255)
+ except Exception as ex:
+ parser.error("Error parsing respawn: %s" % ex)
+
+ # terminator
+ special.append(0xff)
+ # size
+ special = [len(special), ] + special
+
+ map_ents[m].extend([t, x, y])
+ if special is not None:
+ if isinstance(special, (tuple, list)):
+ map_ents[m].extend(special)
+ else:
+ map_ents[m].append(special)
+
+ if args.max_ents:
+ for i, weight in map_ents_w.items():
+ if weight > args.max_ents:
+ parser.error("map %i has %d entities, max is %d" %
+ (i, weight, args.max_ents))
+
+ if args.max_bytes:
+ for i, byts in map_ents_bytes.items():
+ if byts > args.max_bytes:
+ parser.error("map %i entities are %d bytes, max is %d" %
+ (i, byts, args.max_bytes))
+
+ # append the entities to the map data
+ for i in range(len(out)):
+ if not out[i]:
+ continue
+ elif map_ents[i]:
+ out[i].extend(map_ents[i])
+ out[i][2] += len(map_ents[i])
+ # terminator
+ out[i].append(0xff)
+ out[i][2] += 1
+
+ if args.reverse:
+ out.reverse()
+
+ if args.bin:
+ for i, block in enumerate(out):
+ filename = path.join(args.dir, "%s%02d.bin" % (args.id, i))
+ with open(filename, "wb") as fd:
+ if i in empty:
+ fd.write(struct.pack("<B", 0))
+ else:
+ fd.write(bytearray(block))
+
+ if not args.quiet:
+ screen_with_data = len(out) - len(empty)
+ total_bytes = sum(len(b) if b else 0 for b in out)
+ print("%s: %s (%d screens, %d bytes, %.2f bytes avg)" % (
+ path.basename(sys.argv[0]), args.id,
+ screen_with_data, total_bytes, total_bytes / screen_with_data),
+ file=sys.stderr)
+ return
+
+ print("#ifndef _%s_H" % args.id.upper())
+ print("#define _%s_H" % args.id.upper())
+ print("/* compressed: %s */" % args.aplib)
+ print("#define WMAPS %d\n" % (mw // args.rw))
+ print("#define MAPS %d\n" % len(out))
+
+ print("#ifdef LOCAL")
+
+ # includes a map table for fast access
+ data_out = ""
+ for i, block in enumerate(out):
+ if not isinstance(block, list):
+ continue
+ data_out_part = ""
+ for part in range(0, len(block), args.rw // 2):
+ if data_out_part:
+ data_out_part += ",\n"
+ data_out_part += ', '.join(
+ ["0x%02x" % byte for byte in block[part: part + args.rw // 2]])
+ data_out += "const unsigned char %s_%d[%d] = {\n" % (
+ args.id, i, len(block))
+ data_out += data_out_part + "\n};\n"
+
+ data_out += "const unsigned char * const %s[%d] = { " % (args.id, len(out))
+ data_out += ', '.join(
+ ["%s_%d" % (args.id,
+ i) if i not in empty else "(unsigned char *)0" for i in range(len(out))])
+ data_out += " };\n"
+ print(data_out)
+
+ print("#else")
+ print("extern const unsigned char * const %s[%d];\n" % (args.id, len(out)))
+
+ print("#endif // LOCAL")
+ print("#endif // _%s_H" % args.id.upper())
+
+ if not args.quiet:
+ screen_with_data = len(out) - len(empty)
+ total_bytes = sum(len(b) if b else 0 for b in out)
+ print("%s: %s (%d screens, %d bytes, %.2f bytes avg)" % (
+ path.basename(sys.argv[0]), args.id,
+ screen_with_data, total_bytes, total_bytes / screen_with_data),
+ file=sys.stderr)
+
+
+if __name__ == "__main__":
+ try:
+ main()
+ except Exception as ex:
+ print("FATAL: %s\n***" % ex, file=sys.stderr)
+ traceback.print_exc(ex)
+ sys.exit(1)
diff --git a/tools/pandocfilter-pygments.py b/tools/pandocfilter-pygments.py
new file mode 100755
index 0000000..ee678f8
--- /dev/null
+++ b/tools/pandocfilter-pygments.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python3
+"""
+From: https://github.com/DoomHammer/pandocfilter-pygments
+
+Requires:
+
+ - pygments
+ - pandocfilters
+"""
+
+from pandocfilters import toJSONFilter, RawBlock
+from pygments import highlight
+from pygments.lexers import (get_lexer_by_name, guess_lexer, TextLexer)
+from pygments.formatters import get_formatter_by_name
+
+def pygmentize(key, value, format, meta):
+ if key == 'CodeBlock':
+ [[ident, classes, keyvals], code] = value
+ lexer = None
+ for klass in classes:
+ try:
+ lexer = get_lexer_by_name(klass)
+ break
+ except:
+ pass
+ if lexer is None:
+ try:
+ lexer = guess_lexer(code)
+ except:
+ lexer = TextLexer()
+ if format == "html5":
+ format = "html"
+ return [RawBlock(format, highlight(code, lexer, get_formatter_by_name(format)))]
+
+if __name__ == "__main__":
+ toJSONFilter(pygmentize)
+
diff --git a/tools/png2sprites.py b/tools/png2sprites.py
new file mode 100755
index 0000000..a554433
--- /dev/null
+++ b/tools/png2sprites.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 by Juan J. Martinez <jjm@usebox.net>
+#
+# 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.
+#
+
+from argparse import ArgumentParser
+from PIL import Image
+
+__version__ = "1.0"
+
+DEF_W = 16
+DEF_H = 16
+
+TRANS = (28, 28, 28)
+
+
+def to_hex_list_str(src):
+ out = ""
+ for i in range(0, len(src), 8):
+ out += ', '.join(["0x%02x" % b for b in src[i:i + 8]]) + ',\n'
+ return out
+
+
+def to_hex_list_str_asm(src):
+ out = ""
+ for i in range(0, len(src), 8):
+ out += '\tdb ' + ', '.join(["#%02x" % b for b in src[i:i + 8]])
+ out += '\n'
+ return out
+
+
+def main():
+
+ parser = ArgumentParser(description="PNG to MSX spites",
+ epilog="Copyright (C) 2019 Juan J Martinez <jjm@usebox.net>",
+ )
+
+ parser.add_argument(
+ "--version", action="version", version="%(prog)s " + __version__)
+ parser.add_argument("-i", "--id", dest="id", default="sprites", type=str,
+ help="variable name (default: sprites)")
+ parser.add_argument("-a", "--asm", dest="asm", action="store_true",
+ help="ASM output (default: C header)")
+
+ parser.add_argument("image", help="image to convert")
+
+ args = parser.parse_args()
+
+ try:
+ image = Image.open(args.image)
+ except IOError:
+ parser.error("failed to open the image")
+
+ if image.mode != "RGB":
+ parser.error("not a RGB image")
+
+ (w, h) = image.size
+
+ if w % DEF_W or h % DEF_H:
+ parser.error("%s size is not multiple of sprite size (%s, %s)" %
+ (args.image, DEF_W, DEF_H))
+
+ data = image.getdata()
+
+ out = []
+ for y in range(0, h, DEF_H):
+ for x in range(0, w, DEF_W):
+ tile = [data[x + i + ((y + j) * w)]
+ for j in range(DEF_H) for i in range(DEF_W)]
+ cols = set([c for c in tile if c != TRANS])
+
+ if not cols:
+ continue
+
+ for c in cols:
+ frame = []
+ for i, j in ((0, 0), (0, 8), (8, 0), (8, 8)):
+ for m in range(8):
+ byte = 0
+ p = 7
+ for k in range(8):
+ b = 1 if tile[i + (j + m) * 16 + k] == c else 0
+ byte |= b << p
+ p -= 1
+ frame.append(byte)
+ out.append(frame)
+
+ if args.asm:
+ print("%s_LEN = %d" % (args.id.upper(), len(out) * 32))
+ print("%s_FRAMES = %d\n" % (args.id.upper(), len(out)))
+ print("%s:\n" % args.id)
+
+ for i, frame in enumerate(out):
+ print("%s_frame%d:" % (args.id, i))
+ print(to_hex_list_str_asm(frame))
+ else:
+ print("#ifndef _%s_H" % args.id.upper())
+ print("#define _%s_H\n" % args.id.upper())
+ print("#define %s_LEN %d\n" % (args.id.upper(), len(out) * 32))
+ print("#define %s_FRAMES %d\n" % (args.id.upper(), len(out)))
+
+ data_out = ""
+ for i, frame in enumerate(out):
+ data_out += '{\n' + to_hex_list_str(frame) + '}'
+ if i + 1 < len(out):
+ data_out += ',\n'
+
+ print("#ifdef LOCAL")
+ print("const unsigned char %s[%d][%d] = {\n%s\n};\n" % (
+ args.id, len(out), len(out[0]), data_out))
+
+ print("#else\n")
+ print("extern const unsigned char %s[%d][%d];" %
+ (args.id, len(out), len(out[0])))
+ print("#endif // LOCAL\n")
+ print("#endif // _%s_H\n" % args.id.upper())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/png2tiles.py b/tools/png2tiles.py
new file mode 100755
index 0000000..eee28e4
--- /dev/null
+++ b/tools/png2tiles.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 by Juan J. Martinez <jjm@usebox.net>
+#
+# 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.
+#
+
+from argparse import ArgumentParser
+from PIL import Image
+from collections import defaultdict
+
+__version__ = "1.0"
+
+DEF_W = 8
+DEF_H = 8
+
+# TOSHIBA palette
+MSX_COLS = [
+ (255, 0, 255),
+ (0, 0, 0),
+ (102, 204, 102),
+ (136, 238, 136),
+ (68, 68, 221),
+ (119, 119, 255),
+ (187, 85, 85),
+ (119, 221, 221),
+ (221, 102, 102),
+ (255, 119, 119),
+ (204, 204, 85),
+ (238, 238, 136),
+ (85, 170, 85),
+ (187, 85, 187),
+ (204, 204, 204),
+ (238, 238, 238),
+]
+
+
+def to_hex_list_str(src):
+ out = ""
+ for i in range(0, len(src), 8):
+ out += ', '.join(["0x%02x" % b for b in src[i:i + 8]]) + ",\n"
+ return out
+
+
+def to_hex_list_str_asm(src):
+ out = ""
+ for i in range(0, len(src), 8):
+ out += '\tdb ' + ', '.join(["#%02x" % b for b in src[i:i + 8]])
+ out += '\n'
+ return out
+
+
+def main():
+
+ parser = ArgumentParser(description="PNG to MSX tiles",
+ epilog="Copyright (C) 2019 Juan J Martinez <jjm@usebox.net>",
+ )
+
+ parser.add_argument(
+ "--version", action="version", version="%(prog)s " + __version__)
+ parser.add_argument("-i", "--id", dest="id", default="tileset", type=str,
+ help="variable name (default: tileset)")
+ parser.add_argument("-a", "--asm", dest="asm", action="store_true",
+ help="ASM output (default: C header)")
+ parser.add_argument("--no-colors", dest="no_colors", action="store_true",
+ help="don't include colors")
+
+ parser.add_argument("image", help="image to convert")
+
+ args = parser.parse_args()
+
+ try:
+ image = Image.open(args.image)
+ except IOError:
+ parser.error("failed to open the image")
+
+ if image.mode != "RGB":
+ parser.error("not a RGB image")
+
+ (w, h) = image.size
+
+ if w % DEF_W or h % DEF_H:
+ parser.error("%s size is not multiple of tile size (%s, %s)" %
+ (args.image, DEF_W, DEF_H))
+
+ data = image.getdata()
+
+ color_idx = defaultdict(list)
+ color = []
+ out = []
+ ntiles = 0
+ for y in range(0, h, DEF_H):
+ for x in range(0, w, DEF_W):
+ # tile data
+ tile = [data[x + i + ((y + j) * w)]
+ for j in range(DEF_H) for i in range(DEF_W)]
+
+ # get the attibutes of the tile
+ # FIXME: this may not be right
+ for i in range(0, len(tile), DEF_W):
+ cols = list(set(tile[i:i + DEF_W]))
+
+ if len(cols) > 2:
+ parser.error(
+ "tile %d (%d, %d) has more than two colors: %r" % (
+ ntiles, x, y, cols))
+ elif len(cols) == 1:
+ cols.append(MSX_COLS[1])
+
+ for c in cols:
+ if c not in MSX_COLS:
+ parser.error(
+ "tile %d (%d, %d) has a color not in the"
+ " expected MSX palette: %r" % (ntiles, x, y, c))
+
+ # each tile has two color attributes per row
+ color_idx[ntiles * DEF_H + i // DEF_W] = cols
+ color.append(
+ (MSX_COLS.index(cols[1]) << 4) | MSX_COLS.index(cols[0]))
+
+ frame = []
+ for i in range(0, len(tile), 8):
+ byte = 0
+ p = 7
+ for k in range(8):
+ # 0 or 1 is determined by the order in the color attributes
+ # for that row
+ byte |= color_idx[
+ ntiles * DEF_H + i // DEF_W].index(tile[i + k]) << p
+ p -= 1
+
+ frame.append(byte)
+ ntiles += 1
+ out.extend(frame)
+
+ if ntiles > 256:
+ parser.error("more than 256 tiles")
+
+ if args.asm:
+ print(";; %d tiles\n" % ntiles)
+ print("%s:" % args.id)
+ print(to_hex_list_str_asm(out))
+
+ if not args.no_colors:
+ print("%s_col:" % args.id)
+ print(to_hex_list_str_asm(color))
+ else:
+ print("#ifndef _%s_H" % args.id.upper())
+ print("#define _%s_H\n" % args.id.upper())
+ print("/* %d tiles */\n" % ntiles)
+
+ data_out = to_hex_list_str(out)
+ print("#ifdef LOCAL")
+ print("const unsigned char %s[%d] = {\n%s\n};\n" %
+ (args.id, len(out), data_out))
+
+ if not args.no_colors:
+ color_out = to_hex_list_str(color)
+ print("const unsigned char %s_colors[%d] = {\n%s\n};\n" % (
+ args.id, len(color), color_out))
+
+ print("#else\n")
+ print("extern const unsigned char %s[%d];" % (args.id, len(out)))
+
+ if not args.no_colors:
+ print("extern const unsigned char %s_colors[%d];" % (
+ args.id, len(color)))
+
+ print("#endif // LOCAL\n")
+ print("#endif // _%s_H\n" % args.id.upper())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/rasm/Makefile b/tools/rasm/Makefile
new file mode 100644
index 0000000..7d6be31
--- /dev/null
+++ b/tools/rasm/Makefile
@@ -0,0 +1,12 @@
+all: rasm
+
+rasm: rasm_v0120.c
+ gcc -s -O2 $< -o $@ -lm -lrt -march=native
+ cp rasm ../../bin
+ rm -f rasm
+
+clean:
+ rm -f rasm
+
+.PHONY: all clean
+
diff --git a/tools/rasm/decrunch/aplib_z80_todo.asm b/tools/rasm/decrunch/aplib_z80_todo.asm
new file mode 100644
index 0000000..6843a14
--- /dev/null
+++ b/tools/rasm/decrunch/aplib_z80_todo.asm
@@ -0,0 +1,190 @@
+;Z80 Version by Dan Weiss
+;Call depack.
+;hl = source
+;de = dest
+
+ap_bits: .db 0
+ap_byte: .db 0
+lwm: .db 0
+r0: .dw 0
+
+ap_getbit:
+ push bc
+ ld bc,(ap_bits)
+ rrc c
+ jr nc,ap_getbit_continue
+ ld b,(hl)
+ inc hl
+ap_getbit_continue:
+ ld a,c
+ and b
+ ld (ap_bits),bc
+ pop bc
+ ret
+
+ap_getbitbc: ;doubles BC and adds the read bit
+ sla c
+ rl b
+ call ap_getbit
+ ret z
+ inc bc
+ ret
+
+ap_getgamma:
+ ld bc,1
+ap_getgammaloop:
+ call ap_getbitbc
+ call ap_getbit
+ jr nz,ap_getgammaloop
+ ret
+
+
+depack:
+ ;hl = source
+ ;de = dest
+ ldi
+ xor a
+ ld (lwm),a
+ inc a
+ ld (ap_bits),a
+
+aploop:
+ call ap_getbit
+ jp z, apbranch1
+ call ap_getbit
+ jr z, apbranch2
+ call ap_getbit
+ jr z, apbranch3
+ ;LWM = 0
+ xor a
+ ld (lwm),a
+ ;get an offset
+ ld bc,0
+ call ap_getbitbc
+ call ap_getbitbc
+ call ap_getbitbc
+ call ap_getbitbc
+ ld a,b
+ or c
+ jr nz,apbranch4
+ xor a ;write a 0
+ ld (de),a
+ inc de
+ jr aploop
+apbranch4:
+ ex de,hl ;write a previous bit (1-15 away from dest)
+ push hl
+ sbc hl,bc
+ ld a,(hl)
+ pop hl
+ ld (hl),a
+ inc hl
+ ex de,hl
+ jr aploop
+apbranch3:
+ ;use 7 bit offset, length = 2 or 3
+ ;if a zero is encountered here, it's EOF
+ ld c,(hl)
+ inc hl
+ rr c
+ ret z
+ ld b,2
+ jr nc,ap_dont_inc_b
+ inc b
+ap_dont_inc_b:
+ ;LWM = 1
+ ld a,1
+ ld (lwm),a
+
+ push hl
+ ld a,b
+ ld b,0
+ ;R0 = c
+ ld (r0),bc
+ ld h,d
+ ld l,e
+ or a
+ sbc hl,bc
+ ld c,a
+ ldir
+ pop hl
+ jr aploop
+apbranch2:
+ ;use a gamma code * 256 for offset, another gamma code for length
+ call ap_getgamma
+ dec bc
+ dec bc
+ ld a,(lwm)
+ or a
+ jr nz,ap_not_lwm
+ ;bc = 2?
+ ld a,b
+ or c
+ jr nz,ap_not_zero_gamma
+ ;if gamma code is 2, use old r0 offset, and a new gamma code for length
+ call ap_getgamma
+ push hl
+ ld h,d
+ ld l,e
+ push bc
+ ld bc,(r0)
+ sbc hl,bc
+ pop bc
+ ldir
+ pop hl
+ jr ap_finishup
+
+ap_not_zero_gamma:
+ dec bc
+ap_not_lwm:
+ ;do I even need this code?
+ ;bc=bc*256+(hl), lazy 16bit way
+ ld b,c
+ ld c,(hl)
+ inc hl
+ ld (r0),bc
+ push bc
+ call ap_getgamma
+ ex (sp),hl
+ ;bc = len, hl=offs
+ push de
+ ex de,hl
+ ;some comparison junk for some reason
+ ld hl,31999
+ or a
+ sbc hl,de
+ jr nc,skip1
+ inc bc
+skip1:
+ ld hl,1279
+ or a
+ sbc hl,de
+ jr nc,skip2
+ inc bc
+skip2:
+ ld hl,127
+ or a
+ sbc hl,de
+ jr c,skip3
+ inc bc
+ inc bc
+skip3:
+ ;bc = len, de = offs, hl=junk
+ pop hl
+ push hl
+ or a
+ sbc hl,de
+ pop de
+ ;hl=dest-offs, bc=len, de = dest
+ ldir
+ pop hl
+ap_finishup:
+ ld a,1
+ ld (lwm),a
+ jp aploop
+
+apbranch1:
+ ldi
+ xor a
+ ld (lwm),a
+ jp aploop
diff --git a/tools/rasm/decrunch/deexo.asm b/tools/rasm/decrunch/deexo.asm
new file mode 100644
index 0000000..6a4a7ac
--- /dev/null
+++ b/tools/rasm/decrunch/deexo.asm
@@ -0,0 +1,118 @@
+;Exomizer 2 Z80 decoder
+; by Metalbrain
+;
+; optimized by Antonio Villena and Urusergi (169 bytes)
+;
+; compression algorithm by Magnus Lind
+
+;input: hl=compressed data start
+; de=uncompressed destination start
+;
+; you may change exo_mapbasebits to point to any free buffer
+;
+;ATTENTION!
+;A huge speed boost (around 14%) can be gained at the cost of only 5 bytes.
+;If you want this, replace all instances of "call exo_getbit" with "srl a" followed by
+;"call z,exo_getbit", and remove the first two instructions in exo_getbit routine.
+
+Macro Mizoumizeur
+
+@deexo: ld iy, @exo_mapbasebits+11
+ ld a, (hl)
+ inc hl
+ ld b, 52
+ push de
+ cp a
+@exo_initbits: ld c, 16
+ jr nz, @exo_get4bits
+ ld ixl, c
+ ld de, 1 ;DE=b2
+@exo_get4bits: srl a: call z, @exo_getbit ;get one bit
+ rl c
+ jr nc, @exo_get4bits
+ inc c
+ push hl
+ ld hl, 1
+ ld (iy+41), c ;bits[i]=b1 (and opcode 41 == add hl,hl)
+@exo_setbit: dec c
+ jr nz, @exo_setbit-1 ;jump to add hl,hl instruction
+ ld (iy-11), e
+ ld (iy+93), d ;base[i]=b2
+ add hl, de
+ ex de, hl
+ inc iy
+ pop hl
+ dec ixl
+ djnz @exo_initbits
+ pop de
+ jr @exo_mainloop
+@exo_literalrun: ld e, c ;DE=1
+@exo_getbits: dec b
+ ret z
+@exo_getbits1: srl a : call z,@exo_getbit
+ rl e
+ rl d
+ jr nc, @exo_getbits
+ ld b, d
+ ld c, e
+ pop de
+@exo_literalcopy:ldir
+@exo_mainloop: inc c
+ srl a : call z,@exo_getbit ;literal?
+ jr c, @exo_literalcopy
+ ld c, 239
+@exo_getindex: srl a : call z,@exo_getbit
+ inc c
+ jr nc,@exo_getindex
+ ret z
+ push de
+ ld d, b
+ jp p, @exo_literalrun
+ ld iy, @exo_mapbasebits-229
+ call @exo_getpair
+ push de
+ rlc d
+ jr nz, @exo_dontgo
+ dec e
+ ld bc, 512+32 ;2 bits, 48 offset
+ jr z, @exo_goforit
+ dec e ;2?
+@exo_dontgo: ld bc, 1024+16 ;4 bits, 32 offset
+ jr z, @exo_goforit
+ ld de, 0
+ ld c, d ;16 offset
+@exo_goforit: call @exo_getbits1
+ ld iy, @exo_mapbasebits+27
+ add iy, de
+ call @exo_getpair
+ pop bc
+ ex (sp), hl
+ push hl
+ sbc hl, de
+ pop de
+ ldir
+ pop hl
+ jr @exo_mainloop ;Next!
+
+@exo_getpair: add iy, bc
+ ld e, d
+ ld b, (iy+41)
+ call @exo_getbits
+ ex de, hl
+ ld c, (iy-11)
+ ld b, (iy+93)
+ add hl, bc ;Always clear C flag
+ ex de, hl
+ ret
+
+@exo_getbit: ; srl a
+ ;ret nz
+ ld a, (hl)
+ inc hl
+ rra
+ ret
+
+@exo_mapbasebits: defs 156 ;tables for bits, baseL, baseH
+
+Mend
+
diff --git a/tools/rasm/decrunch/dzx7_turbo.asm b/tools/rasm/decrunch/dzx7_turbo.asm
new file mode 100644
index 0000000..779ced5
--- /dev/null
+++ b/tools/rasm/decrunch/dzx7_turbo.asm
@@ -0,0 +1,80 @@
+; -----------------------------------------------------------------------------
+; ZX7 decoder by Einar Saukas & Urusergi
+; "Turbo" version (88 bytes, 25% faster)
+; -----------------------------------------------------------------------------
+; Parameters:
+; HL: source address (compressed data)
+; DE: destination address (decompressing)
+; -----------------------------------------------------------------------------
+
+dzx7_turbo:
+ ld a, $80
+dzx7t_copy_byte_loop:
+ ldi ; copy literal byte
+dzx7t_main_loop:
+ add a, a ; check next bit
+ call z, dzx7t_load_bits ; no more bits left?
+ jr nc, dzx7t_copy_byte_loop ; next bit indicates either literal or sequence
+
+; determine number of bits used for length (Elias gamma coding)
+ push de
+ ld bc, 1
+ ld d, b
+dzx7t_len_size_loop:
+ inc d
+ add a, a ; check next bit
+ call z, dzx7t_load_bits ; no more bits left?
+ jr nc, dzx7t_len_size_loop
+ jp dzx7t_len_value_start
+
+; determine length
+dzx7t_len_value_loop:
+ add a, a ; check next bit
+ call z, dzx7t_load_bits ; no more bits left?
+ rl c
+ rl b
+ jr c, dzx7t_exit ; check end marker
+dzx7t_len_value_start:
+ dec d
+ jr nz, dzx7t_len_value_loop
+ inc bc ; adjust length
+
+; determine offset
+ ld e, (hl) ; load offset flag (1 bit) + offset value (7 bits)
+ inc hl
+ defb $cb, $33 ; opcode for undocumented instruction "SLL E" aka "SLS E"
+ jr nc, dzx7t_offset_end ; if offset flag is set, load 4 extra bits
+ add a, a ; check next bit
+ call z, dzx7t_load_bits ; no more bits left?
+ rl d ; insert first bit into D
+ add a, a ; check next bit
+ call z, dzx7t_load_bits ; no more bits left?
+ rl d ; insert second bit into D
+ add a, a ; check next bit
+ call z, dzx7t_load_bits ; no more bits left?
+ rl d ; insert third bit into D
+ add a, a ; check next bit
+ call z, dzx7t_load_bits ; no more bits left?
+ ccf
+ jr c, dzx7t_offset_end
+ inc d ; equivalent to adding 128 to DE
+dzx7t_offset_end:
+ rr e ; insert inverted fourth bit into E
+
+; copy previous sequence
+ ex (sp), hl ; store source, restore destination
+ push hl ; store destination
+ sbc hl, de ; HL = destination - offset - 1
+ pop de ; DE = destination
+ ldir
+dzx7t_exit:
+ pop hl ; restore source address (compressed data)
+ jp nc, dzx7t_main_loop
+
+dzx7t_load_bits:
+ ld a, (hl) ; load another group of 8 bits
+ inc hl
+ rla
+ ret
+
+; -----------------------------------------------------------------------------
diff --git a/tools/rasm/decrunch/exomizer3megachur.asm b/tools/rasm/decrunch/exomizer3megachur.asm
new file mode 100644
index 0000000..ea1973e
--- /dev/null
+++ b/tools/rasm/decrunch/exomizer3megachur.asm
@@ -0,0 +1,210 @@
+;Exomizer 2 Z80 decoder
+;Copyright (C) 2008-2016 by Jaime Tejedor Gomez (Metalbrain)
+;
+;Optimized by Antonio Villena and Urusergi (169 bytes)
+;
+;Compression algorithm by Magnus Lind
+;
+; 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
+;
+;
+;input- hl=compressed data start
+; de=uncompressed destination start
+;
+; you may change exo_mapbasebits to point to any free buffer
+;
+;ATTENTION!
+;A huge speed boost (around 14%) can be gained at the cost of only 5 bytes.
+;If you want this, replace all instances of "call exo_getbit" with "srl a" followed by
+;"call z,exo_getbit", and remove the first two instructions in exo_getbit routine.
+; ---------------------------
+; modified by Megachur in 2018
+; ---------------------------
+; hl -> compressed data start
+; de -> uncompressed destination start
+; ---------------------------
+
+;EXO_BACKWARD equ 1
+ENABLE_MEXO_GETBIT equ 1
+
+list:EXOMIZER_ADDRESS:nolist
+; ---------------------------
+MACRO MEXO_GETBIT
+ srl a
+ jr nz,@1
+ ld a,(hl)
+ IFDEF EXO_BACKWARD
+ dec hl
+ ELSE
+ inc hl
+ ENDIF
+ rra
+@1
+ENDM
+
+deexo:
+ ld iy,exo_mapbasebits+11
+ ld a,(hl)
+
+ IFDEF EXO_BACKWARD
+ dec hl
+ ELSE
+ inc hl
+ ENDIF
+
+ ld b,52
+ push de
+ cp a
+
+exo_initbits:
+ ld c,16
+ jr nz,exo_get4bits
+ ld ixl,c
+ ld de,1 ;DE=b2
+
+exo_get4bits:
+ IFDEF ENABLE_MEXO_GETBIT
+ MEXO_GETBIT
+ ELSE
+ srl a:call z,exo_getbit ;call exo_getbit ;get one bit
+ ENDIF
+ rl c
+ jr nc,exo_get4bits
+ inc c
+ push hl
+ ld hl,1
+ ld (iy+41),c ;bits[i]=b1 (and opcode 41 == add hl,hl)
+
+exo_setbit:
+ dec c
+ jr nz,exo_setbit-1 ;jump to add hl,hl instruction
+ ld (iy-11),e
+ ld (iy+93),d ;base[i]=b2
+ add hl,de
+ ex de,hl
+ inc iy
+ pop hl
+ dec ixl
+ djnz exo_initbits
+ pop de
+ jr exo_mainloop
+
+exo_literalrun:
+ ld e,c ;DE=1
+
+exo_getbits:
+ dec b
+ ret z
+
+exo_getbits1:
+ IFDEF ENABLE_MEXO_GETBIT
+ MEXO_GETBIT
+ ELSE
+ srl a:call z,exo_getbit ;call exo_getbit
+ ENDIF
+ rl e
+ rl d
+ jr nc,exo_getbits
+ ld b,d
+ ld c,e
+ pop de
+
+exo_literalcopy:
+ IFDEF EXO_BACKWARD
+ lddr
+ ELSE
+ ldir
+ ENDIF
+exo_mainloop:
+ inc c
+ IFDEF ENABLE_MEXO_GETBIT
+ MEXO_GETBIT
+ ELSE
+ srl a:call z,exo_getbit ;call exo_getbit ;literal?
+ ENDIF
+ jr c,exo_literalcopy
+ ld c,239
+exo_getindex:
+ IFDEF ENABLE_MEXO_GETBIT
+ MEXO_GETBIT
+ ELSE
+ srl a:call z,exo_getbit ;call exo_getbit
+ ENDIF
+ inc c
+ jr nc,exo_getindex
+ ret z
+ push de
+ ld d,b
+ jp p,exo_literalrun
+ ld iy,exo_mapbasebits-229
+ call exo_getpair
+ push de
+ rlc d
+ jr nz,exo_dontgo
+ dec e
+ ld bc,512+32 ;2 bits,48 offset
+ jr z,exo_goforit
+ dec e ;2?
+exo_dontgo:
+ ld bc,1024+16 ;4 bits,32 offset
+ jr z,exo_goforit
+ ld de,0
+ ld c,d ;16 offset
+exo_goforit:
+ call exo_getbits1
+ ld iy,exo_mapbasebits+27
+ add iy,de
+ call exo_getpair
+ pop bc
+ ex (sp),hl
+ IFDEF EXO_BACKWARD
+ ex de,hl
+ add hl,de
+ lddr
+ ELSE
+ push hl
+ sbc hl,de
+ pop de
+ ldir
+ ENDIF
+ pop hl
+ jr exo_mainloop ;Next!
+exo_getpair:
+ add iy,bc
+ ld e,d
+ ld b,(iy+41)
+ call exo_getbits
+ ex de,hl
+ ld c,(iy-11)
+ ld b,(iy+93)
+ add hl,bc ;Always clear C flag
+ ex de,hl
+ ret
+
+ IFDEF ENABLE_MEXO_GETBIT
+ ELSE
+exo_getbit:
+; srl a
+; ret nz
+ ld a,(hl)
+ inc hl
+ rra
+ ret
+ ENDIF
+
+exo_mapbasebits:
+ ds 156,#00 ;tables for bits,baseL,baseH
+; ---------------------------
+list:EXOMIZER_ADDRESS_LENGTH equ $-EXOMIZER_ADDRESS:nolist \ No newline at end of file
diff --git a/tools/rasm/decrunch/lz48decrunch_v006.asm b/tools/rasm/decrunch/lz48decrunch_v006.asm
new file mode 100644
index 0000000..750b571
--- /dev/null
+++ b/tools/rasm/decrunch/lz48decrunch_v006.asm
@@ -0,0 +1,113 @@
+;
+; LZ48 decrunch
+;
+; hl compressed data adress
+; de output adress of data
+;
+
+
+org #8000
+
+; CALL #8000,source,destination
+di
+
+; parameters
+ld h,(ix+3)
+ld l,(ix+2)
+ld d,(ix+1)
+ld e,(ix+0)
+
+call LZ48_decrunch
+
+ei
+ret
+
+
+
+
+
+LZ48_decrunch
+ldi
+ld b,0
+
+nextsequence
+ld a,(hl)
+inc hl
+ld lx,a
+and #F0
+jr z,lzunpack ; no litteral bytes
+rrca
+rrca
+rrca
+rrca
+
+ld c,a
+cp 15 ; more bytes for length?
+jr nz,copyliteral
+
+getadditionallength
+ld a,(hl)
+inc hl
+inc a
+jr nz,lengthnext
+inc b
+dec bc
+jr getadditionallength
+lengthnext
+dec a
+add a,c
+ld c,a
+ld a,b
+adc a,0
+ld b,a ; bc=length
+
+copyliteral
+ldir
+
+lzunpack
+ld a,lx
+and #F
+add 3
+ld c,a
+cp 18 ; more bytes for length?
+jr nz,readoffset
+
+getadditionallengthbis
+ld a,(hl)
+inc hl
+inc a
+jr nz,lengthnextbis
+inc b
+dec bc
+jr getadditionallengthbis
+lengthnextbis
+dec a
+add a,c
+ld c,a
+ld a,b
+adc a,0
+ld b,a ; bc=length
+
+readoffset
+; read encoded offset
+ld a,(hl)
+inc a
+ret z ; LZ48 end with zero offset
+inc hl
+push hl
+ld l,a
+ld a,e
+sub l
+ld l,a
+ld a,d
+sbc a,0
+ld h,a
+; source=dest-copyoffset
+
+copykey
+ldir
+
+pop hl
+jr nextsequence
+
+
diff --git a/tools/rasm/decrunch/lz49decrunch_v001.asm b/tools/rasm/decrunch/lz49decrunch_v001.asm
new file mode 100644
index 0000000..7f811b5
--- /dev/null
+++ b/tools/rasm/decrunch/lz49decrunch_v001.asm
@@ -0,0 +1,138 @@
+;
+; LZ48 decrunch
+; input
+; hl compressed data adress
+; de output adress of data
+;
+; output
+; hl last adress of compressed data read (you must inc once for LZ48 stream)
+; de last adress of decrunched data write +1
+; bc always 3
+; a always zero
+; lx undetermined
+; flags (inc a -> 0)
+
+org #8000
+
+; CALL #8000,source,destination
+di
+
+; parameters
+ld h,(ix+3)
+ld l,(ix+2)
+ld d,(ix+1)
+ld e,(ix+0)
+
+call LZ49_decrunch
+
+ei
+ret
+
+
+
+
+
+LZ49_decrunch
+ldi
+ld b,0
+
+nextsequence
+ld a,(hl)
+inc hl
+ld lx,a
+and #70
+jr z,lzunpack ; no litteral bytes
+rrca
+rrca
+rrca
+rrca
+
+ld c,a
+cp 7 ; more bytes for length?
+jr nz,copyliteral
+
+getadditionallength
+ld a,(hl)
+inc hl
+inc a
+jr nz,lengthnext
+inc b
+dec bc
+jr getadditionallength
+lengthnext
+dec a
+add a,c
+ld c,a
+ld a,b
+adc a,0
+ld b,a ; bc=length
+
+copyliteral
+ldir
+
+lzunpack
+ld a,lx
+and #F
+add 3
+ld c,a
+cp 18 ; more bytes for length?
+jr nz,readoffset
+
+getadditionallengthbis
+ld a,(hl)
+inc hl
+inc a
+jr nz,lengthnextbis
+inc b
+dec bc
+jr getadditionallengthbis
+lengthnextbis
+dec a
+add a,c
+ld c,a
+ld a,b
+adc a,0
+ld b,a ; bc=length
+
+readoffset
+ld a,lx
+add a
+jr c,extendedoffset
+; read encoded offset
+ld a,(hl)
+inc a
+ret z ; LZ48 end with zero offset
+inc hl
+push hl
+ld l,a
+ld a,e
+sub l
+ld l,a
+ld a,d
+sbc a,0
+ld h,a
+; source=dest-copyoffset
+ldir
+pop hl
+jr nextsequence
+
+extendedoffset
+ld a,(hl)
+inc hl
+push hl
+inc a
+ld l,a
+ld a,e
+sub l
+ld l,a
+ld a,d
+sbc a,1
+ld h,a
+; source=dest-copyoffset
+ldir
+pop hl
+jr nextsequence
+
+
+
+
diff --git a/tools/rasm/decrunch/lz4_docent.asm b/tools/rasm/decrunch/lz4_docent.asm
new file mode 100644
index 0000000..a0b2188
--- /dev/null
+++ b/tools/rasm/decrunch/lz4_docent.asm
@@ -0,0 +1,118 @@
+; decompress raw lz4 data packet
+; on entry hl - start of packed buffer, de - destination buffer, bc - size of packed data
+LZ4_decompress_raw:
+ push de ; store original destination pointer
+ push hl ; store start of compressed data source
+ add hl,bc ; calculate end address of compressed block
+ ld b,h ; move end address of compressed data to bc
+ ld c,l
+ pop hl ; restore start of compressed data source
+ push bc ; store end address of compessed data
+; now hl - start of packed buffer, de - destination, bc - end of packed buffer
+ ld b,0 ; clear b, c is set later
+
+; get decompression token
+LZ4_GetToken:
+ xor a ; reset c flag for sbc later
+ ld a,(hl) ; read token
+ inc hl
+ push af ; store token
+; unpack 4 high bits to get the length of literal
+ rlca
+ rlca
+ rlca
+ rlca
+; copy literals
+ and #f ; token can be max 15 - mask out unimportant bits
+ jr z,LZ4_skipcalc ; there is no literals, skip calculation of literal size
+ ld c,a ; set the count for calculation
+ cp #f ; if literal size <15
+ jr nz, LZ4_copyliterals ; copy literal, else
+; calculate total literal size by adding contents of following bytes
+ push de ; store destination
+ ex de,hl
+; a = size of literal to copy, de=pointer to data to be added
+ ld h,0 ; set hl with size of literal to copy
+ ld l,a
+
+LZ4_calcloop:
+ ld a,(de) ; get additional literal size to add
+ inc de
+ ld c,a ; set bc to the length of literal
+ add hl,bc ; add it to the total literal length
+ cp #ff ; if literal=255
+ jr z,LZ4_calcloop ; continue calculating the total literal size
+ ld b,h ; store total literal size to copy in bc
+ ld c,l
+ ex de,hl ; hl now contains current compressed data pointer
+ pop de ; restore destination to de
+
+LZ4_copyliterals:
+ ldir ; copy literal to destination
+
+LZ4_skipcalc:
+; check for end of compressed data
+ pop af ; restore token, carry is cleared because of xor a at the beginning of GetToken
+ pop bc ; restore end address of compressed data
+ push hl ; store current compressed data pointer
+ sbc hl, bc ; check if we reached the end of compressed data buffer
+ pop hl ; restore current compressed data pointer
+ jr z,LZ4_decompress_success ; decompression finished
+ push bc ; store end address of compressed data
+
+; Copy Matches
+ and #f ; token can be max 15 - mask out unimportant bits. resets also c flag for sbc later
+; get the offset
+ ld c,(hl)
+ inc hl
+ ld b,(hl) ; bc now contains the offset
+ inc hl
+ push hl ; store current compressed data pointer
+ push de ; store destination pointer
+
+ ex de,hl
+ sbc hl,bc ; calculate from the offset the new decompressed data source to copy from
+; hl contains new copy source, de source ptr
+
+ ld b,0 ; load bc with the token
+ ld c,a
+ cp #f ; if matchlength <15
+ jr nz, LZ4_copymatches ; copy matches. else
+
+; calculate total matchlength by adding additional bytes
+ push hl ; store current decompressed data source
+; a = size of match to copy, de= pointer to data to be added
+ ld h,0 ; set hl with initial matchlength to copy
+ ld l,a
+LZ4_calcloop2:
+ ld a,(de) ; get additional matchlength to add
+ inc de
+ ld c,a ; set bc to the matchlength
+ add hl,bc ; add it to the total match length
+ cp #ff ; if matchlength=255
+ jr z,LZ4_calcloop2 ; continue calculating the total match length
+ ld b,h ; store total matchlength to copy in bc
+ ld c,l
+ pop hl ; restore current decompressed data source
+ pop af ; set stack to proper position by restoring destination pointer temporarily into af
+ ex de,hl
+ ex (sp),hl ; update current compressed data pointer on the stack to the new value from de
+ ex de,hl
+ push af ; restore stack
+
+LZ4_copymatches:
+ pop de ; restore destination pointer
+ inc bc ; add base length of 4 to get the correct size of matchlength
+ inc bc
+ inc bc
+ inc bc
+ ldir ; copy match
+ pop hl ; restore current compressed data source
+ jr LZ4_GetToken ; continue decompression
+LZ4_decompress_success:
+ pop hl ; store destination pointer
+ sbc hl,de ; calculate the number of decompressed bytes
+ xor a ; clear exit code
+ ret
+
+
diff --git a/tools/rasm/exomizer.h b/tools/rasm/exomizer.h
new file mode 100644
index 0000000..1f3c7ca
--- /dev/null
+++ b/tools/rasm/exomizer.h
@@ -0,0 +1,4942 @@
+/*
+
+Warning! This is a modified version of original sources!
+
+To sum up:
+- all include files and C sources were merged in a single file
+- existing logs were removed (except error logs)
+- main were removed and wrapper added
+
+
+*/
+
+#ifndef ALREADY_INCLUDED_CALLBACK
+#define ALREADY_INCLUDED_CALLBACK
+
+/*
+ * 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);
+typedef void cb_fprint(FILE *f, const void *a);
+
+#endif
+#ifndef INCLUDED_INT
+#define INCLUDED_INT
+
+/*
+ * 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;
+
+#endif
+#ifndef ALREADY_INCLUDED_PROGRESS
+#define ALREADY_INCLUDED_PROGRESS
+
+/*
+ * 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[1], char *msg, int start, int end);
+
+void progress_bump(struct progress p[1], int pos);
+
+void progress_free(struct progress p[1]);
+
+#endif
+#ifndef ALREADY_INCLUDED_CHUNKPOOL
+#define ALREADY_INCLUDED_CHUNKPOOL
+
+/*
+ * 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.
+ *
+ */
+
+#define CHUNKPOOL_CHUNKS_MAX 64
+
+struct chunkpool {
+ int chunk_size;
+ int chunk;
+ int chunk_pos;
+ int chunk_max;
+ void *chunks[64];
+};
+
+void
+chunkpool_init(struct chunkpool *ctx, int 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);
+
+#endif
+#ifndef ALREADY_INCLUDED_MATCH
+#define ALREADY_INCLUDED_MATCH
+/*
+ * 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.
+ *
+ */
+
+struct match {
+ unsigned short int offset;
+ unsigned short int len;
+ struct match *next;
+};
+
+typedef struct match match[1];
+typedef struct match *matchp;
+typedef const struct match *const_matchp;
+
+struct pre_calc {
+ struct match_node *single;
+ const struct match *cache;
+};
+
+struct match_ctx {
+ struct chunkpool m_pool[1];
+ struct pre_calc (*info)[1];
+ unsigned short int *rle;
+ unsigned short int *rle_r;
+ const unsigned char *buf;
+ int len;
+ int max_offset;
+ int max_len;
+};
+
+typedef struct match_ctx match_ctx[1];
+typedef struct match_ctx *match_ctxp;
+
+//void match_ctx_init(match_ctx ctx, /* IN/OUT */ struct membuf *inbuf, /* IN */ int max_len, /* IN */ int max_offset, /* IN */ int use_imprecise_rle); /* IN */
+
+void match_ctx_free(match_ctx ctx); /* IN/OUT */
+
+/* this needs to be called with the indexes in
+ * reverse order */
+const_matchp matches_get(match_ctx ctx, /* IN/OUT */
+ int index); /* IN */
+
+void match_delete(match_ctx ctx, /* IN/OUT */
+ matchp mp); /* IN */
+
+struct matchp_cache_enum {
+ match_ctxp ctx;
+ const_matchp next;
+ match tmp1;
+ match tmp2;
+ int pos;
+};
+
+typedef struct matchp_cache_enum matchp_cache_enum[1];
+typedef struct matchp_cache_enum *matchp_cache_enump;
+
+void matchp_cache_get_enum(match_ctx ctx, /* IN */
+ matchp_cache_enum mpce); /* IN/OUT */
+
+typedef const_matchp matchp_enum_get_next_f(void *matchp_enum); /* IN/OUT */
+
+const_matchp matchp_cache_enum_get_next(void *matchp_cache_enum); /* IN */
+
+#endif
+#ifndef ALREADY_INCLUDED_OUTPUT
+#define ALREADY_INCLUDED_OUTPUT
+
+/*
+ * 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.
+ *
+ */
+
+struct _output_ctx {
+ unsigned int bitbuf;
+ int pos;
+ int start;
+ struct membuf *buf;
+};
+
+typedef struct _output_ctx output_ctx[1];
+typedef struct _output_ctx *output_ctxp;
+
+void output_ctx_init(output_ctx ctx, struct membuf *out); /* IN/OUT */
+
+unsigned int output_get_pos(output_ctx ctx); /* IN */
+
+void output_byte(output_ctx ctx, /* IN/OUT */
+ unsigned char byte); /* IN */
+
+void output_word(output_ctx ctx, /* IN/OUT */
+ unsigned short int word); /* IN */
+
+void output_bits_flush(output_ctx ctx); /* IN/OUT */
+
+void output_bits(output_ctx ctx, /* IN/OUT */
+ int count, /* IN */
+ int val); /* IN */
+
+void output_gamma_code(output_ctx ctx, /* IN/OUT */
+ int code); /* IN */
+#endif
+#ifndef ALREADY_INCLUDED_SEARCH
+#define ALREADY_INCLUDED_SEARCH
+
+/*
+ * 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.
+ *
+ */
+
+struct _search_node {
+ int index;
+ match match;
+ unsigned int total_offset;
+ float total_score;
+ struct _search_node *prev;
+};
+
+typedef struct _search_node search_node[1];
+typedef struct _search_node *search_nodep;
+typedef const struct _search_node *const_search_nodep;
+
+struct _encode_match_data {
+ output_ctxp out;
+ void *priv;
+};
+
+typedef struct _encode_match_data encode_match_data[1];
+typedef struct _encode_match_data *encode_match_datap;
+
+/* 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, output_ctxp out); /* IN */
+
+struct _encode_match_priv {
+ 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;
+
+ output_ctxp out;
+};
+
+typedef struct _encode_match_priv encode_match_priv[1];
+typedef struct _encode_match_priv *encode_match_privp;
+/* end of example */
+
+typedef
+float encode_match_f(const_matchp mp, encode_match_data emd); /* IN */
+
+void search_node_dump(search_nodep snp); /* IN */
+
+void search_node_free(search_nodep snp); /* IN/OUT */
+
+search_nodep search_buffer(match_ctx ctx, /* IN */
+ encode_match_f * f, /* IN */
+ encode_match_data emd,
+ int use_literal_sequences); /* IN */
+
+struct _matchp_snp_enum {
+ const_search_nodep startp;
+ const_search_nodep currp;
+};
+
+typedef struct _matchp_snp_enum matchp_snp_enum[1];
+typedef struct _matchp_snp_enum *matchp_snp_enump;
+
+void matchp_snp_get_enum(const_search_nodep snp, /* IN */
+ matchp_snp_enum snpe); /* IN/OUT */
+
+const_matchp matchp_snp_enum_get_next(void *matchp_snp_enum);
+
+#endif
+#ifndef ALREADY_INCLUDED_RADIX
+#define ALREADY_INCLUDED_RADIX
+/*
+ * 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.
+ *
+ */
+
+typedef struct _radix_node *radix_nodep;
+
+struct _radix_root {
+ int depth;
+ radix_nodep root;
+ struct chunkpool mem[1];
+};
+
+typedef struct _radix_root radix_root[1];
+typedef struct _radix_root *radix_rootp;
+
+
+typedef void free_callback(void *data, void *priv);
+
+/* *f will be called even for null pointers */
+void radix_tree_free(radix_root rr, /* IN */
+ free_callback * f, /* IN */
+ void *priv); /* IN */
+
+void radix_tree_init(radix_root rr); /* IN */
+
+void radix_node_set(radix_root rr, /* IN */
+ unsigned int index, /* IN */
+ void *data); /* IN */
+
+void *radix_node_get(radix_root rr, /* IN */
+ unsigned int index); /* IN */
+
+#endif
+#ifndef MEMBUF_IO_ALREADY_INCLUDED
+#define MEMBUF_IO_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, 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.
+ *
+ */
+
+void read_file(const char *name, struct membuf *buf);
+void write_file(const char *name, struct membuf *buf);
+
+#endif
+#ifndef ALREADY_INCLUDED_MEMBUF
+#define ALREADY_INCLUDED_MEMBUF
+
+/*
+ * 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.
+ *
+ */
+
+#define STATIC_MEMBUF_INIT {0, 0, 0}
+
+struct membuf {
+ void *buf;
+ int len;
+ int size;
+};
+
+void membuf_init(struct membuf *sb);
+void membuf_clear(struct membuf *sb);
+void membuf_free(struct membuf *sb);
+void membuf_new(struct membuf **sbp);
+void membuf_delete(struct membuf **sbp);
+/* Gets the length of data put into the membuf */
+int membuf_memlen(const struct membuf *sb);
+void membuf_truncate(struct membuf *sb, int len);
+
+/* returns the new len or < 0 if failure */
+int membuf_trim(struct membuf *sb, int pos);
+
+void *membuf_memcpy(struct membuf *sb, int offset, const void *mem, int len);
+void *membuf_append(struct membuf *sb, const void *mem, int len);
+void *membuf_append_char(struct membuf *sb, char c);
+void *membuf_insert(struct membuf *sb, int offset, const void *mem, int len);
+void membuf_remove(struct membuf *sb, int offset, int len);
+/* Grows the capacity if it's less than the given size */
+void membuf_atleast(struct membuf *sb, int size);
+/* Skrinks the capacity if it's greater than the given size */
+void membuf_atmost(struct membuf *sb, int size);
+/* Gets the current capacity of the membuf */
+int membuf_get_size(const struct membuf *sb);
+/* Gets a pointer to the internal buffer. Don't dereferece it beyond
+ * its size. */
+void *membuf_get(const struct membuf *sb);
+
+#endif
+#ifndef INCLUDED_PARSE
+#define INCLUDED_PARSE
+
+/*
+ * 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.
+ *
+ */
+
+#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 initial_symbol_dump(int level, const char *symbol);
+
+struct membuf *new_initial_named_buffer(const char *name);
+
+int assemble(struct membuf *source, struct membuf *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 membuf *out, struct vec *mem);
+void asm_src_buffer_push(struct membuf *buf);
+
+int assembleSinglePass(struct membuf *source, struct membuf *dest);
+
+#endif
+#ifndef ALREADY_INCLUDED_OPTIMAL
+#define ALREADY_INCLUDED_OPTIMAL
+
+/*
+ * 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.
+ *
+ */
+
+float optimal_encode(const_matchp mp, /* IN */
+ encode_match_data emp); /* IN */
+
+void optimal_init(encode_match_data emp); /* IN/OUT */
+
+void optimal_free(encode_match_data emd); /* IN */
+
+void optimal_optimize(encode_match_data emd, /* IN/OUT */
+ matchp_enum_get_next_f * f, /* IN */
+ void *priv); /* IN */
+
+void optimal_encoding_import(encode_match_data emd, /* IN/OUT */
+ const char *encoding); /* IN */
+
+const char *
+optimal_encoding_export(encode_match_data emd); /* IN */
+
+void optimal_dump(int level, encode_match_data emp); /* IN */
+
+void optimal_out(output_ctx out, /* IN/OUT */
+ encode_match_data emd); /* IN */
+
+#endif
+#ifndef ALREADY_INCLUDED_VEC
+#define ALREADY_INCLUDED_VEC
+
+/*
+ * 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.
+ *
+ */
+#define STATIC_VEC_INIT(EL_SIZE) {(EL_SIZE), STATIC_MEMBUF_INIT, 1}
+
+struct vec {
+ size_t elsize;
+ struct membuf 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_count(const struct vec *p);
+void *vec_get(const struct vec *p, int index);
+
+/**
+ * Returns a pointer to the set area or null if the index is out of
+ * bounds.
+ **/
+void *vec_set(struct vec *p, int index, const void *in);
+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);
+
+void vec_get_iterator(const struct vec *p, struct vec_iterator *i);
+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);
+
+#endif
+#ifndef ALREADY_INCLUDED_MAP
+#define ALREADY_INCLUDED_MAP
+
+/*
+ * 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.
+ *
+ */
+
+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);
+
+#endif
+#ifndef NAMED_BUFFER_INCLUDED
+#define NAMED_BUFFER_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, 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 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 membuf *new_named_buffer(struct named_buffer *nb, const char *name);
+struct membuf *get_named_buffer(struct named_buffer *nb, const char *name);
+
+#endif
+#ifndef ALREADY_INCLUDED_LOG
+#define ALREADY_INCLUDED_LOG
+/*
+ * 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.
+ *
+ */
+
+
+#ifndef __GNUC__
+#define __attribute__(x) /*NOTHING*/
+#endif
+
+enum log_level {
+ LOG_MIN = -99,
+ LOG_FATAL = -40,
+ LOG_ERROR = -30,
+ LOG_WARNING = -20,
+ 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;
+
+#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, stderr); \
+ 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); \
+ log_log_default M; \
+ } \
+} while(0)
+
+
+void hex_dump(int level, unsigned char *p, int len);
+
+#endif
+#ifndef ALREADY_INCLUDED_GETFLAG
+#define ALREADY_INCLUDED_GETFLAG
+/*
+ * 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.
+ *
+ */
+
+extern int flagind;
+extern int flagflag;
+extern const char *flagarg;
+
+int getflag(int argc, char *argv[], const char *flags);
+
+#endif
+#ifndef ALREADY_INCLUDED_EXODEC
+#define ALREADY_INCLUDED_EXODEC
+
+/*
+ * 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 dec_table
+{
+ unsigned char table_bit[3];
+ unsigned char table_off[3];
+ 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 membuf *outbuf;
+ unsigned int bitbuf;
+ /* dep_table */
+ struct dec_table t[1];
+ int bits_read;
+};
+
+/* returns the encoding */
+char *
+dec_ctx_init(struct dec_ctx ctx[1],
+ struct membuf *inbuf, struct membuf *outbuf);
+
+void
+dec_ctx_free(struct dec_ctx ctx[1]);
+
+void dec_ctx_decrunch(struct dec_ctx ctx[1]);
+
+#endif
+#ifndef EXO_UTIL_ALREADY_INCLUDED
+#define EXO_UTIL_ALREADY_INCLUDED
+
+/*
+ * Copyright (c) 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.
+ *
+ */
+
+
+/*
+ * 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);
+
+struct load_info
+{
+ int basic_txt_start; /* in */
+ int basic_var_start; /* out */
+ int run; /* out */
+ int start; /* out */
+ int end; /* out */
+};
+
+void load_located(char *filename, unsigned char mem[65536],
+ struct load_info *info);
+
+int str_to_int(const char *str, int *value);
+
+const char *fixup_appl(char *appl);
+
+#endif
+#ifndef EXO_HELPER_ALREADY_INCLUDED
+#define EXO_HELPER_ALREADY_INCLUDED
+
+/*
+ * Copyright (c) 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.
+ *
+ */
+
+
+#define CRUNCH_OPTIONS_DEFAULT {NULL, 65535, 65535, 65535, 1, 0}
+
+struct common_flags
+{
+ struct crunch_options *options;
+ const char *outfile;
+};
+
+#define CRUNCH_FLAGS "cCe:m:M:p:o:qv"
+#define BASE_FLAGS "o:qv"
+
+void print_crunch_flags(enum log_level level, const char *default_outfile);
+
+void print_base_flags(enum log_level level, const char *default_outfile);
+
+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 *exported_encoding;
+ int max_passes;
+ int max_len;
+ int max_offset;
+ int use_literal_sequences;
+ int use_imprecise_rle;
+};
+
+struct crunch_info
+{
+ int literal_sequences_used;
+ int needed_safety_offset;
+};
+
+void print_license(void);
+
+void crunch_backwards(struct membuf *inbuf,
+ struct membuf *outbuf,
+ struct crunch_options *options, /* IN */
+ struct crunch_info *info); /* OUT */
+
+void exocrunch(struct membuf *inbuf,
+ struct membuf *outbuf,
+ struct crunch_options *options, /* IN */
+ struct crunch_info *info); /* OUT */
+
+void exodecrunch(int level,
+ struct membuf *inbuf,
+ struct membuf *outbuf);
+
+void decrunch_backwards(int level,
+ struct membuf *inbuf,
+ struct membuf *outbuf);
+
+void reverse_buffer(char *start, int len);
+#endif
+#ifndef ALREADY_INCLUDED_DESFX
+#define ALREADY_INCLUDED_DESFX
+
+/*
+ * 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.
+ *
+ */
+
+u16 decrunch_sfx(u8 mem[65536], u16 run, u16 *start, u16 *end);
+
+#endif
+
+/*
+ * 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.
+ *
+ */
+#define DEFAULT_OUTFILE "a.out"
+#define OUTPUT_FLAG_REVERSE 1
+
+/*
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+*/
+
+#ifdef WIN32
+#define vsnprintf _vsnprintf
+#endif
+#ifdef DJGPP
+#define vsnprintf(A, B, C, D) vsprintf((A),(C),(D))
+#endif
+
+#define BAR_LENGTH 64
+
+//extern struct membuf sfxdecr[];
+
+
+#define RADIX_TREE_NODE_RADIX 11U
+#define RADIX_TREE_NODE_MASK ((1U << RADIX_TREE_NODE_RADIX) - 1U)
+
+struct _radix_node {
+ struct _radix_node *rn;
+};
+
+void radix_tree_init(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, radix_nodep 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(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(radix_rootp rrp, /* IN */
+ unsigned int index, /* IN */
+ void *data) /* IN */
+{
+ radix_nodep rnp;
+ radix_nodep *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(radix_root rr, /* IN */
+ unsigned int index) /* IN */
+{
+ radix_nodep 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;
+}
+
+void search_node_free(search_nodep snp) /* IN */
+{
+ /* emty now since snp:s are stored in an array */
+}
+
+search_nodep search_buffer(match_ctx ctx, /* IN */
+ encode_match_f * f, /* IN */
+ encode_match_data emd, /* IN */
+ int use_literal_sequences)
+{
+ static struct membuf backing[1] = { STATIC_MEMBUF_INIT };
+ static search_node *snp_arr;
+ const_matchp mp = NULL;
+ search_nodep snp;
+ search_nodep best_copy_snp;
+ int best_copy_len;
+
+ search_nodep best_rle_snp;
+
+ int len = ctx->len + 1;
+
+
+ membuf_atleast(backing, len * sizeof(search_node));
+ snp_arr = membuf_get(backing);
+ memset(snp_arr, 0, len * sizeof(search_node));
+
+ --len;
+ snp = snp_arr[len];
+ snp->index = len;
+ snp->match->offset = 0;
+ snp->match->len = 0;
+ snp->total_offset = 0;
+ snp->total_score = 0;
+ snp->prev = NULL;
+
+ best_copy_snp = snp;
+ best_copy_len = 0.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. */
+ while (len > 0 && (mp = matches_get(ctx, len - 1)) != NULL)
+ {
+ float prev_score;
+ float prev_offset_sum;
+
+ if(use_literal_sequences)
+ {
+ /* check if we can do even better with copy */
+ snp = snp_arr[len];
+ if(best_copy_snp->total_score+best_copy_len * 8.0 -
+ snp->total_score > 0.0 || best_copy_len > 65535)
+ {
+ /* found a better copy endpoint */
+ best_copy_snp = snp;
+ best_copy_len = 0.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;
+
+
+ if(snp->total_score > total_copy_score )
+ {
+ match local_mp;
+ /* here it is good to just copy instead of crunch */
+
+
+ local_mp->len = best_copy_len;
+ local_mp->offset = 0;
+ local_mp->next = NULL;
+ snp->total_score = total_copy_score;
+ snp->total_offset = best_copy_snp->total_offset;
+ snp->prev = best_copy_snp;
+ *snp->match = *local_mp;
+ }
+ }
+ /* end of copy optimization */
+ }
+
+ /* check if we can do rle */
+ snp = snp_arr[len];
+ if(best_rle_snp == NULL ||
+ snp->index + 65535 < 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;
+ }
+ 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;
+ match rle_mp;
+
+
+ /* snp and best_rle_snp is the same rle area,
+ * let's see which is best */
+#undef NEW_STYLE
+#ifdef NEW_STYLE
+ rle_mp->len = best_rle_snp->index - snp->index;
+#else
+ rle_mp->len = ctx->rle[best_rle_snp->index];
+#endif
+ rle_mp->offset = 1;
+ best_rle_score = f(rle_mp, emd);
+ total_best_rle_score = best_rle_snp->total_score +
+ best_rle_score;
+
+#ifdef NEW_STYLE
+ snp_rle_score = 0.0;
+#else
+ rle_mp->len = ctx->rle[snp->index];
+ rle_mp->offset = 1;
+ snp_rle_score = f(rle_mp, emd);
+#endif
+ 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 */
+ best_rle_snp = snp;
+ }
+ }
+ if(best_rle_snp != NULL && best_rle_snp != snp)
+ {
+ float rle_score;
+ float total_rle_score;
+ /* check if rle is better */
+ match local_mp;
+ local_mp->len = best_rle_snp->index - snp->index;
+ local_mp->offset = 1;
+
+ rle_score = f(local_mp, emd);
+ total_rle_score = best_rle_snp->total_score + rle_score;
+
+ if(snp->total_score > total_rle_score)
+ {
+ /*here it is good to do rle instead of crunch */
+ snp->total_score = total_rle_score;
+ snp->total_offset = best_rle_snp->total_offset + 1;
+ snp->prev = best_rle_snp;
+
+ *snp->match = *local_mp;
+ }
+ }
+ /* end of rle optimization */
+
+
+ prev_score = snp_arr[len]->total_score;
+ prev_offset_sum = snp_arr[len]->total_offset;
+ while (mp != NULL)
+ {
+ matchp next;
+ int end_len;
+
+ match tmp;
+
+ next = mp->next;
+ end_len = 1;
+#if 0
+ if(next != NULL)
+ {
+ end_len = next->len + (next->offset > 0);
+ }
+#endif
+ *tmp = *mp;
+#if 1
+ tmp->next = NULL;
+#endif
+ for(tmp->len = mp->len; tmp->len >= end_len; --(tmp->len))
+ {
+ float score;
+ float total_score;
+ unsigned int total_offset;
+
+ score = f(tmp, emd);
+ total_score = prev_score + score;
+ total_offset = prev_offset_sum + tmp->offset;
+ snp = snp_arr[len - tmp->len];
+
+ if ((total_score < 100000000.0) &&
+ (snp->match->len == 0 ||
+ total_score < snp->total_score ||
+ (total_score == snp->total_score &&
+#if 1
+ (tmp->offset == 0 ||
+ (snp->match->len == tmp->len &&
+ (total_offset <= snp->total_offset))))))
+#endif
+ {
+ snp->index = len - tmp->len;
+
+ *snp->match = *tmp;
+ snp->total_offset = total_offset;
+ snp->total_score = total_score;
+ snp->prev = snp_arr[len];
+ }
+ //LOG(LOG_DUMP, ("\n"));
+ }
+ mp = next;
+ }
+
+ /* slow way to get to the next node for cur */
+ --len;
+ ++best_copy_len;
+ }
+ if(len > 0 && mp == NULL)
+ {
+ LOG(LOG_ERROR, ("No matches at len %d.\n", len));
+ }
+
+
+ return snp_arr[0];
+}
+
+void matchp_snp_get_enum(const_search_nodep snp, /* IN */
+ matchp_snp_enum snpe) /* IN/OUT */
+{
+ snpe->startp = snp;
+ snpe->currp = snp;
+}
+
+const_matchp matchp_snp_enum_get_next(void *matchp_snp_enum)
+{
+ matchp_snp_enump snpe;
+ const_matchp val;
+
+ snpe = matchp_snp_enum;
+
+ val = NULL;
+ while (snpe->currp != NULL && val == NULL)
+ {
+ val = snpe->currp->match;
+ snpe->currp = snpe->currp->prev;
+ }
+
+ if (snpe->currp == NULL)
+ {
+ snpe->currp = snpe->startp;
+ }
+ return val;
+}
+
+void
+chunkpool_init(struct chunkpool *ctx, int size)
+{
+ ctx->chunk_size = size;
+ ctx->chunk = -1;
+ ctx->chunk_max = (0x1fffff / size) * size;
+ ctx->chunk_pos = ctx->chunk_max;
+}
+
+void
+chunkpool_free2(struct chunkpool *ctx, cb_free *f)
+{
+ while(ctx->chunk >= 0)
+ {
+ if(f != NULL)
+ {
+ do
+ {
+ ctx->chunk_pos -= ctx->chunk_size;
+ f((char*)ctx->chunks[ctx->chunk] + ctx->chunk_pos);
+ }
+ while(ctx->chunk_pos > 0);
+ ctx->chunk_pos = ctx->chunk_max;
+ }
+ free(ctx->chunks[ctx->chunk]);
+ ctx->chunk -= 1;
+ }
+ ctx->chunk_size = -1;
+ ctx->chunk_max = -1;
+ ctx->chunk_pos = -1;
+}
+
+void
+chunkpool_free(struct chunkpool *ctx)
+{
+ chunkpool_free2(ctx, NULL);
+}
+
+void *
+chunkpool_malloc(struct chunkpool *ctx)
+{
+ void *p;
+ if(ctx->chunk_pos == ctx->chunk_max)
+ {
+ void *m;
+ if(ctx->chunk == CHUNKPOOL_CHUNKS_MAX - 1)
+ {
+ LOG(LOG_ERROR, ("out of chunks in file %s, line %d\n",
+ __FILE__, __LINE__));
+ LOG(LOG_BRIEF, ("chunk_size %d\n", ctx->chunk_size));
+ LOG(LOG_BRIEF, ("chunk_max %d\n", ctx->chunk_max));
+ LOG(LOG_BRIEF, ("chunk %d\n", ctx->chunk));
+ exit(-1);
+ }
+ m = malloc(ctx->chunk_max);
+ if (m == NULL)
+ {
+ LOG(LOG_ERROR, ("out of memory error in file %s, line %d\n",
+ __FILE__, __LINE__));
+ exit(-1);
+ }
+ ctx->chunk += 1;
+ ctx->chunks[ctx->chunk] = m;
+ ctx->chunk_pos = 0;
+ }
+ p = (char*)ctx->chunks[ctx->chunk] + ctx->chunk_pos;
+ ctx->chunk_pos += ctx->chunk_size;
+ return p;
+}
+
+void *
+chunkpool_calloc(struct chunkpool *ctx)
+{
+ void *p = chunkpool_malloc(ctx);
+ memset(p, 0, ctx->chunk_size);
+ return p;
+}
+
+static struct crunch_options default_options[1] = { CRUNCH_OPTIONS_DEFAULT };
+
+int do_output(match_ctx ctx,
+ search_nodep snp,
+ encode_match_data emd,
+ encode_match_f * f,
+ struct membuf *outbuf,
+ int *literal_sequences_used)
+{
+ int pos;
+ int pos_diff;
+ int max_diff;
+ int diff;
+ int copy_used = 0;
+ output_ctxp old;
+ output_ctx out;
+
+ output_ctx_init(out, outbuf);
+ old = emd->out;
+ emd->out = out;
+
+ pos = output_get_pos(out);
+
+ pos_diff = pos;
+ max_diff = 0;
+
+ 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;
+ }
+
+ while (snp != NULL)
+ {
+ const_matchp mp;
+
+ mp = snp->match;
+ if (mp != NULL && mp->len > 0)
+ {
+ if (mp->offset == 0)
+ {
+ if(mp->len == 1)
+ {
+ /* literal */
+ output_byte(out, ctx->buf[snp->index]);
+ output_bits(out, 1, 1);
+ } else
+ {
+ int i;
+ for(i = 0; i < mp->len; ++i)
+ {
+ output_byte(out, ctx->buf[snp->index + i]);
+ }
+ output_bits(out, 16, mp->len);
+ output_gamma_code(out, 17);
+ output_bits(out, 1, 0);
+ copy_used = 1;
+ }
+ } else
+ {
+ f(mp, emd);
+ output_bits(out, 1, 0);
+ }
+
+ pos_diff += mp->len;
+ diff = output_get_pos(out) - pos_diff;
+ if(diff > max_diff)
+ {
+ max_diff = diff;
+ }
+ }
+ snp = snp->prev;
+ }
+
+ /* output header here */
+ optimal_out(out, emd);
+
+ output_bits_flush(out);
+
+ emd->out = old;
+
+ if(literal_sequences_used != NULL)
+ {
+ *literal_sequences_used = copy_used;
+ }
+
+ return max_diff;
+}
+
+search_nodep
+do_compress(match_ctx ctx, encode_match_data emd,
+ const char *exported_encoding,
+ int max_passes,
+ int use_literal_sequences)
+{
+ matchp_cache_enum mpce;
+ matchp_snp_enum snpe;
+ search_nodep snp;
+ search_nodep best_snp;
+ int pass;
+ float size;
+ float old_size;
+ char prev_enc[100];
+ const char *curr_enc;
+
+ pass = 1;
+ prev_enc[0] = '\0';
+
+ if(exported_encoding != NULL)
+ {
+ optimal_encoding_import(emd, exported_encoding);
+ }
+ else
+ {
+ matchp_cache_get_enum(ctx, mpce);
+ optimal_optimize(emd, matchp_cache_enum_get_next, mpce);
+ }
+
+ best_snp = NULL;
+ old_size = 100000000.0;
+
+ for (;;)
+ {
+ snp = search_buffer(ctx, optimal_encode, emd,
+ use_literal_sequences);
+ if (snp == NULL)
+ {
+ LOG(LOG_ERROR, ("error: search_buffer() returned NULL\n"));
+ exit(-1);
+ }
+
+ size = snp->total_score;
+
+ if (size >= old_size)
+ {
+ search_node_free(snp);
+ break;
+ }
+
+ if (best_snp != NULL)
+ {
+ search_node_free(best_snp);
+ }
+ best_snp = snp;
+ old_size = size;
+ ++pass;
+
+ if(pass > max_passes)
+ {
+ break;
+ }
+
+ optimal_free(emd);
+ optimal_init(emd);
+
+ matchp_snp_get_enum(snp, snpe);
+ optimal_optimize(emd, matchp_snp_enum_get_next, snpe);
+
+ curr_enc = optimal_encoding_export(emd);
+ if (strcmp(curr_enc, prev_enc) == 0)
+ {
+ break;
+ }
+ strcpy(prev_enc, curr_enc);
+ }
+
+ return best_snp;
+}
+
+void match_ctx_init(match_ctx ctx, /* IN/OUT */
+ struct membuf *inbuf, /* IN */
+ int max_len, /* IN */
+ int max_offset, /* IN */
+ int use_imprecise_rle); /* IN */
+void crunch_backwards(struct membuf *inbuf,
+ struct membuf *outbuf,
+ struct crunch_options *options, /* IN */
+ struct crunch_info *info) /* OUT */
+{
+ static match_ctx ctx;
+ encode_match_data emd;
+ search_nodep snp;
+ int outlen;
+ int safety;
+ int copy_used;
+
+ if(options == NULL)
+ {
+ options = default_options;
+ }
+
+ outlen = membuf_memlen(outbuf);
+ emd->out = NULL;
+ optimal_init(emd);
+
+ //LOG(LOG_NORMAL, (" Length of indata: %d bytes.\n", membuf_memlen(inbuf)));
+
+ match_ctx_init(ctx, inbuf, options->max_len, options->max_offset,
+ options->use_imprecise_rle);
+
+
+ emd->out = NULL;
+ optimal_init(emd);
+
+ snp = do_compress(ctx, emd, options->exported_encoding,
+ options->max_passes, options->use_literal_sequences);
+
+ safety = do_output(ctx, snp, emd, optimal_encode, outbuf, &copy_used);
+ //LOG(LOG_NORMAL, (" Length of crunched data: %d bytes.\n",membuf_memlen(outbuf) - outlen));
+
+ optimal_free(emd);
+ search_node_free(snp);
+ match_ctx_free(ctx);
+
+ if(info != NULL)
+ {
+ info->literal_sequences_used = copy_used;
+ info->needed_safety_offset = safety;
+ }
+}
+
+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 exocrunch(struct membuf *inbuf,
+ struct membuf *outbuf,
+ struct crunch_options *options, /* IN */
+ struct crunch_info *info) /* OUT */
+{
+ int outpos;
+ reverse_buffer(membuf_get(inbuf), membuf_memlen(inbuf));
+ outpos = membuf_memlen(outbuf);
+
+ crunch_backwards(inbuf, outbuf, options, info);
+
+ reverse_buffer(membuf_get(inbuf), membuf_memlen(inbuf));
+ reverse_buffer((char*)membuf_get(outbuf) + outpos,
+ membuf_memlen(outbuf) - outpos);
+}
+
+void exodecrunch(int level,
+ struct membuf *inbuf,
+ struct membuf *outbuf)
+{
+ struct dec_ctx ctx[1];
+ char *enc;
+ enc = dec_ctx_init(ctx, inbuf, outbuf);
+
+ LOG(level, (" Encoding: %s\n", enc));
+
+ dec_ctx_decrunch(ctx);
+ dec_ctx_free(ctx);
+}
+
+void decrunch_backwards(int level,
+ struct membuf *inbuf,
+ struct membuf *outbuf)
+{
+ int outpos;
+ reverse_buffer(membuf_get(inbuf), membuf_memlen(inbuf));
+ outpos = membuf_memlen(outbuf);
+
+ exodecrunch(level, inbuf, outbuf);
+
+ reverse_buffer(membuf_get(inbuf), membuf_memlen(inbuf));
+ reverse_buffer((char*)membuf_get(outbuf) + outpos,
+ membuf_memlen(outbuf) - outpos);
+}
+
+void print_license(void)
+{
+}
+
+void print_base_flags(enum log_level level, const char *default_outfile)
+{
+ LOG(level,
+ (" -o <outfile> sets the outfile name, default is \"%s\"\n",
+ default_outfile));
+ LOG(level,
+ (" -q quiet mode, disables 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 enable imprecise rle matching, trades result for speed\n"
+ " -e <encoding> uses the given encoding for crunching\n"
+ " -m <offset> sets the maximum sequence offset, default is 65535\n"
+ " -M <length> sets the maximum sequence length, default is 65535\n"
+ " -p <passes> limits the number of optimization passes, default is 65535\n"));
+ print_base_flags(level, default_outfile);
+}
+
+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_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_BRIEF, *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->use_literal_sequences = 0;
+ break;
+ case 'C':
+ options->use_imprecise_rle = 1;
+ break;
+ case 'e':
+ options->exported_encoding = flag_arg;
+ break;
+ case 'm':
+ if (str_to_int(flag_arg, &options->max_offset) != 0 ||
+ options->max_offset < 0 || options->max_offset >= 65536)
+ {
+ 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) != 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) != 0 ||
+ options->max_passes < 1 || options->max_passes >= 65536)
+ {
+ LOG(LOG_ERROR,
+ ("Error: invalid value for -p option, "
+ "must be in the range of [1 - 65535]\n"));
+ print_usage(appl, LOG_NORMAL, flags->outfile);
+ exit(-1);
+ }
+ break;
+ default:
+ handle_base_flags(flag_char, flag_arg, print_usage,
+ appl, &flags->outfile);
+ }
+}
+
+int find_sys(const unsigned char *buf, int target)
+{
+ int outstart = -1;
+ int state = 1;
+ int i = 0;
+ /* skip link and line number */
+ buf += 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((target == -1 &&
+ (c == 0x9e /* cbm */ ||
+ c == 0x8c /* apple 2*/ ||
+ c == 0xbf /* oric 1*/)) ||
+ c == target)
+ {
+ 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;
+ }
+
+ return outstart;
+}
+
+static int ExoUtil_get_byte(FILE *in)
+{
+ int byte = fgetc(in);
+ if(byte == EOF)
+ {
+ LOG(LOG_ERROR, ("Error: unexpected end of xex-file."));
+ fclose(in);
+ exit(-1);
+ }
+ return byte;
+}
+
+static int get_le_word(FILE *in)
+{
+ int word = ExoUtil_get_byte(in);
+ word |= ExoUtil_get_byte(in) << 8;
+ return word;
+}
+
+static int get_be_word(FILE *in)
+{
+ int word = ExoUtil_get_byte(in) << 8;
+ word |= ExoUtil_get_byte(in);
+ return word;
+}
+
+static
+FILE *
+open_file(char *name, int *load_addr)
+{
+ FILE * in;
+ int is_plain = 0;
+ int is_relocated = 0;
+ int load = -3;
+
+ do
+ {
+ char *load_str;
+ char *at_str;
+
+ in = fopen(name, "rb");
+ if (in != NULL)
+ {
+ /* 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_plain = 1;
+ load_str = at_str;
+ }
+
+ if (load_str == NULL)
+ {
+ /* nope, */
+ break;
+ }
+
+ *load_str = '\0';
+ ++load_str;
+ is_relocated = 1;
+
+ /* relocation was requested */
+ if (str_to_int(load_str, &load) != 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);
+ }
+
+ if(!is_plain)
+ {
+ /* read the prg load address */
+ int prg_load = get_le_word(in);
+ if(!is_relocated)
+ {
+ load = prg_load;
+ /* unrelocated prg loading to $ffff is xex */
+ if(prg_load == 0xffff)
+ {
+ /* differentiate this from relocated $ffff files so it is
+ * possible to override the xex auto-detection. */
+ load = -1;
+ }
+ /* unrelocated prg loading to $1616 is Oric tap */
+ else if(prg_load == 0x1616)
+ {
+ load = -2;
+ }
+ }
+ }
+
+ if(load_addr != NULL)
+ {
+ *load_addr = load;
+ }
+ return in;
+}
+
+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;
+
+ 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)
+ {
+ /* allowed optional header */
+ initial_state:
+ 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;
+ }
+}
+
+static void load_oric_tap(unsigned char mem[65536], FILE *in,
+ struct load_info *info)
+{
+ int c;
+ int autostart;
+ int start, end, len;
+
+ /* read oric tap header */
+
+ /* next byte must be 0x16 as we have already read two and must
+ * have at least three */
+ if(ExoUtil_get_byte(in) != 0x16)
+ {
+ LOG(LOG_ERROR, ("Error: fewer than three lead-in bytes ($16) "
+ "in Oric tap-file header.\n"));
+ fclose(in);
+ exit(-1);
+ }
+ /* optionally more 0x16 bytes */
+ while((c = ExoUtil_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 */
+ ExoUtil_get_byte(in); /* should be 0x0 */
+ ExoUtil_get_byte(in); /* should be 0x0 */
+ ExoUtil_get_byte(in); /* should be 0x0 or 0x80 */
+ autostart = (ExoUtil_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);
+ ExoUtil_get_byte(in); /* should be 0x0 */
+ /* read optional file name */
+ while(ExoUtil_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 */
+ info->start = start;
+ info->end = end;
+ info->run = -1;
+ info->basic_var_start = -1;
+ if(autostart)
+ {
+ info->run = start;
+ }
+ if(info->basic_txt_start >= start &&
+ info->basic_txt_start < end)
+ {
+ info->basic_var_start = end - 1;
+ }
+}
+
+static void load_prg(unsigned char mem[65536], FILE *in,
+ struct load_info *info)
+{
+ int len;
+ len = fread(mem + info->start, 1, 65536 - info->start, in);
+
+ info->end = info->start + len;
+ info->basic_var_start = -1;
+ info->run = -1;
+ if(info->basic_txt_start >= info->start &&
+ info->basic_txt_start < info->end)
+ {
+ info->basic_var_start = info->end;
+ }
+}
+
+void load_located(char *filename, unsigned char mem[65536],
+ struct load_info *info)
+{
+ int load;
+ FILE *in;
+
+ in = open_file(filename, &load);
+ if(load == -1)
+ {
+ /* file is an xex file */
+ load_xex(mem, in, info);
+ }
+ else if(load == -2)
+ {
+ /* file is an oric tap file */
+ load_oric_tap(mem, in, info);
+ }
+ else
+ {
+ /* file is a located plain file or a prg file */
+ info->start = load;
+ load_prg(mem, in, info);
+ }
+ fclose(in);
+
+ LOG(LOG_NORMAL,
+ (" filename: \"%s\", loading from $%04X to $%04X\n",
+ filename, info->start, info->end));
+}
+
+/* returns 0 if ok, 1 otherwise */
+int str_to_int(const char *str, int *value)
+{
+ 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 == '$')
+ {
+ /* a $ prefix specifies base 16 */
+ ++str;
+ base = 16;
+ }
+
+ lval = strtol(str, &str_end, base);
+
+ if(*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;
+ }
+ } 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;
+}
+
+char *get(struct membuf *buf)
+{
+ return membuf_get(buf);
+}
+
+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++];
+
+ return c;
+}
+
+int
+get_bits(struct dec_ctx *ctx, int count)
+{
+ int val;
+
+ val = 0;
+
+ /*printf("get_bits: count = %d", count);*/
+ while(count-- > 0) {
+ if((ctx->bitbuf & 0x1FF) == 1) {
+ ctx->bitbuf = get_byte(ctx) | 0x100;
+ }
+ val <<= 1;
+ val |= ctx->bitbuf & 0x1;
+ ctx->bitbuf >>= 1;
+ /*printf("bit read %d\n", val &1);*/
+ ctx->bits_read++;
+ }
+ /*printf(" val = %d\n", val);*/
+ return val;
+}
+
+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;
+}
+
+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;
+ unsigned int a = 0;
+ unsigned int b = 0;
+
+ tp->table_bit[0] = 2;
+ tp->table_bit[1] = 4;
+ tp->table_bit[2] = 4;
+
+ tp->table_off[0] = 48;
+ tp->table_off[1] = 32;
+ tp->table_off[2] = 16;
+
+ for(i = 0; i < 52; ++i)
+ {
+ if(i & 0xF)
+ {
+ a += 1 << b;
+ } else
+ {
+ a = 1;
+ }
+
+ tp->table_lo[i] = a & 0xFF;
+ tp->table_hi[i] = a >> 8;
+
+ b = get_bits(ctx, 4);
+
+ tp->table_bi[i] = b;
+
+ }
+}
+
+char *
+table_dump(struct dec_table *tp)
+{
+ int i, j;
+ static char buf[100];
+ char *p = buf;
+
+ for(i = 0; i < 16; ++i)
+ {
+ p += sprintf(p, "%X", tp->table_bi[i]);
+ }
+ for(j = 0; j < 3; ++j)
+ {
+ int start;
+ int end;
+ p += sprintf(p, ",");
+ start = tp->table_off[j];
+ end = start + (1 << tp->table_bit[j]);
+ for(i = start; i < end; ++i)
+ {
+ p += sprintf(p, "%X", tp->table_bi[i]);
+ }
+ }
+ return buf;
+}
+
+char *
+dec_ctx_init(struct dec_ctx *ctx, struct membuf *inbuf, struct membuf *outbuf)
+{
+ char *encoding;
+ ctx->bits_read = 0;
+
+ ctx->inbuf = membuf_get(inbuf);
+ ctx->inend = membuf_memlen(inbuf);
+ ctx->inpos = 0;
+
+ ctx->outbuf = outbuf;
+
+ /* init bitbuf */
+ ctx->bitbuf = get_byte(ctx);
+
+ /* init tables */
+ table_init(ctx, ctx->t);
+ encoding = table_dump(ctx->t);
+ return encoding;
+}
+
+void dec_ctx_free(struct dec_ctx *ctx)
+{
+}
+
+void dec_ctx_decrunch(struct dec_ctx ctx[1])
+{
+ int bits;
+ int val;
+ int i;
+ int len;
+ int offset;
+ int src = 0;
+
+ for(;;)
+ {
+ int literal = 0;
+ bits = ctx->bits_read;
+ if(get_bits(ctx, 1))
+ {
+ /* literal */
+ len = 1;
+
+ literal = 1;
+ goto literal;
+ }
+
+ val = get_gamma_code(ctx);
+ if(val == 16)
+ {
+ /* done */
+ break;
+ }
+ if(val == 17)
+ {
+ len = get_bits(ctx, 16);
+ literal = 1;
+
+ goto literal;
+ }
+
+ len = get_cooked_code_phase2(ctx, val);
+
+ i = (len > 3 ? 3 : len) - 1;
+
+ val = ctx->t->table_off[i] + get_bits(ctx, ctx->t->table_bit[i]);
+ offset = get_cooked_code_phase2(ctx, val);
+
+ src = membuf_memlen(ctx->outbuf) - offset;
+
+ literal:
+ do {
+ if(literal)
+ {
+ val = get_byte(ctx);
+ }
+ else
+ {
+ val = get(ctx->outbuf)[src++];
+ }
+ membuf_append_char(ctx->outbuf, val);
+ } while (--len > 0);
+
+ }
+}
+
+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 (flagarg != NULL || flagp[1] != ':')
+ {
+ if (flagarg != NULL && flagp[1] != ':')
+ {
+ /* error, a simple flag with an argument */
+ c = '?';
+ }
+ 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;
+}
+
+struct log_output {
+ enum log_level min;
+ enum log_level max;
+ FILE *stream;
+ 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;
+
+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->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)
+ {
+ /* 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)
+{
+#if 0
+ 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, (","));
+ }
+ }
+#endif
+}
+
+struct match_node {
+ int index;
+ struct match_node *next;
+};
+
+static
+const_matchp matches_calc(match_ctx ctx, /* IN/OUT */
+ int index); /* IN */
+
+matchp match_new(match_ctx ctx, /* IN/OUT */
+ matchp *mpp,
+ int len,
+ int offset)
+{
+ matchp m = chunkpool_malloc(ctx->m_pool);
+
+ if(len == 0)
+ {
+ LOG(LOG_ERROR, ("tried to allocate len0 match.\n"));
+ *(volatile int*)0;
+ }
+ if(len > 65535)
+ {
+ len = 65535;
+ }
+
+ m->len = len;
+ m->offset = offset;
+
+ /* insert new node in list */
+ m->next = *mpp;
+ *mpp = m;
+
+ return m;
+}
+
+
+void match_ctx_init(match_ctx ctx, /* IN/OUT */
+ struct membuf *inbuf, /* IN */
+ int max_len, /* IN */
+ int max_offset, /* IN */
+ int use_imprecise_rle) /* IN */
+{
+ struct match_node *np;
+
+ int buf_len = membuf_memlen(inbuf);
+ const unsigned char *buf = membuf_get(inbuf);
+
+ int c, i;
+ int val;
+
+ 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(match));
+
+ ctx->max_offset = max_offset;
+ ctx->max_len = max_len;
+
+ ctx->buf = buf;
+ ctx->len = buf_len;
+
+ val = buf[0];
+ for (i = 1; i < buf_len; ++i)
+ {
+ if (buf[i] == val)
+ {
+ int len = ctx->rle[i - 1] + 1;
+ if(len > 65535)
+ {
+ len = 0;
+ }
+ ctx->rle[i] = len;
+ } else
+ {
+ ctx->rle[i] = 0;
+ }
+ val = buf[i];
+ }
+
+ for (i = buf_len - 2; i >= 0; --i)
+ {
+ if (ctx->rle[i] < ctx->rle[i + 1])
+ {
+ ctx->rle_r[i] = ctx->rle_r[i + 1] + 1;
+ } else
+ {
+ ctx->rle_r[i] = 0;
+ }
+ }
+
+ /* add extra nodes to rle sequences */
+ for(c = 0; c < 256; ++c)
+ {
+ static char rle_map[65536];
+ struct match_node *prev_np;
+ unsigned short int rle_len;
+
+ /* for each possible rle char */
+ memset(rle_map, 0, sizeof(rle_map));
+ prev_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 (use_imprecise_rle &&
+ 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;
+ }
+
+ ctx->info[i]->single = np;
+ prev_np = np;
+ }
+
+ memset(rle_map, 0, sizeof(rle_map));
+ 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
+ {
+ prev_np = np;
+ }
+
+ if(ctx->rle_r[i] > 0)
+ {
+ continue;
+ }
+ rle_len = ctx->rle[i] + 1;
+ rle_map[rle_len] = 1;
+ }
+ }
+
+
+ for (i = buf_len - 1; i >= 0; --i)
+ {
+ const_matchp matches;
+
+ /* let's populate the cache */
+ matches = matches_calc(ctx, i);
+
+ /* add to cache */
+ ctx->info[i]->cache = matches;
+
+ }
+
+}
+
+void match_ctx_free(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, matchp 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_matchp matches_get(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_matchp matches_calc(match_ctx ctx, /* IN/OUT */
+ int index) /* IN */
+{
+ const unsigned char *buf;
+
+ matchp matches;
+ matchp mp;
+ struct match_node *np;
+
+ buf = ctx->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 <previous len> 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] == buf[pos + offset])
+ {
+#if 1
+ 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;
+#else
+ --len;
+ ++pos;
+#endif
+ }
+ 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 previuos one. let's compare further. */
+ len = mp_len;
+ pos = index - len;
+ while(len <= ctx->max_len &&
+ pos >= 0 && buf[pos] == buf[pos + offset])
+ {
+ LOG(LOG_DUMP, ("2) compared sucesssfully [%d] %d %d\n",
+ index, pos, pos + offset));
+ ++len;
+ --pos;
+ }
+ if(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
+matchp_keep_this(const_matchp mp)
+{
+ int val = 1;
+ /* if we want to ignore this matchp then return true else false */
+ if(mp->len == 1)
+ {
+ if(mp->offset > 32)
+ {
+ val = 0;
+ }
+ }
+ return val;
+}
+
+static
+void
+matchp_cache_peek(struct match_ctx *ctx, int pos,
+ const_matchp *litpp, const_matchp *seqpp,
+ matchp lit_tmp, matchp val_tmp)
+{
+ const_matchp 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)
+ {
+ val_tmp->offset = 1;
+ val_tmp->len = ctx->rle[pos] + 1;
+ val_tmp->next = (matchp)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(matchp_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;
+ 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(matchp_keep_this(tmp2))
+ {
+ LOG(LOG_DEBUG, (", keeping"));
+ *lit_tmp = *tmp2;
+ litp = lit_tmp;
+ }
+ }
+ LOG(LOG_DEBUG, ("\n"));
+ }
+ }
+ val = val->next;
+ }
+ }
+#if 0
+ LOG(LOG_NORMAL, ("[%05d]: ", pos));
+ if(litp == NULL)
+ LOG(LOG_NORMAL, ("litp(NULL)"));
+ else
+ LOG(LOG_NORMAL, ("litp(%d,%d)", litp->len, litp->offset));
+
+ if(seqp == NULL)
+ LOG(LOG_NORMAL, ("seqp(NULL)"));
+ else
+ LOG(LOG_NORMAL, ("seqp(%d,%d)", seqp->len, seqp->offset));
+
+ LOG(LOG_NORMAL, ("\n"));
+#endif
+
+ if(litpp != NULL) *litpp = litp;
+ if(seqpp != NULL) *seqpp = seqp;
+}
+
+void matchp_cache_get_enum(match_ctx ctx, /* IN */
+ matchp_cache_enum mpce) /* IN/OUT */
+{
+ mpce->ctx = ctx;
+ mpce->pos = ctx->len - 1;
+ /*mpce->next = NULL;*/ /* two iterations */
+ mpce->next = (void*)mpce; /* just one */
+}
+
+const_matchp matchp_cache_enum_get_next(void *matchp_cache_enum)
+{
+ const_matchp val, lit, seq;
+ matchp_cache_enump mpce;
+
+ mpce = matchp_cache_enum;
+
+ restart:
+ matchp_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;
+ if(mpce->next == NULL)
+ {
+ mpce->next = (void*)mpce;
+ goto restart;
+ }
+ else
+ {
+ mpce->next = NULL;
+ }
+ }
+ else
+ {
+ if(seq != NULL)
+ {
+ match t1;
+ match t2;
+ const_matchp next;
+ matchp_cache_peek(mpce->ctx, mpce->pos - 1, NULL, &next, t1 ,t2);
+ if(next == NULL ||
+ (next->len + (mpce->next != NULL && next->len < 3) <= seq->len))
+ {
+ /* 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 read_file(const char *name, struct membuf *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);
+ membuf_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 membuf *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(membuf_get(buf), 1, membuf_memlen(buf), out);
+ fclose(out);
+}
+void membuf_init(struct membuf *sb)
+{
+ sb->buf = NULL;
+ sb->len = 0;
+ sb->size = 0;
+}
+void membuf_clear(struct membuf *sb)
+{
+ sb->len = 0;
+}
+void membuf_free(struct membuf *sb)
+{
+ if (sb->buf != NULL)
+ {
+ free(sb->buf);
+ sb->buf = NULL;
+ }
+ sb->len = 0;
+ sb->size = 0;
+}
+
+void membuf_new(struct membuf **sbp)
+{
+ struct membuf *sb;
+
+ sb = malloc(sizeof(struct membuf));
+ if (sb == NULL)
+ {
+ fprintf(stderr, "error, can't allocate memory\n");
+ exit(1);
+ }
+ sb->buf = NULL;
+ sb->len = 0;
+ sb->size = 0;
+
+ *sbp = sb;
+}
+
+void membuf_delete(struct membuf **sbp)
+{
+ struct membuf *sb;
+
+ sb = *sbp;
+ membuf_free(sb);
+ free(sb);
+ sb = NULL;
+ *sbp = sb;
+}
+
+int membuf_memlen(const struct membuf *sb)
+{
+ return sb->len;
+}
+
+void membuf_truncate(struct membuf *sb, int len)
+{
+ sb->len = len;
+}
+
+int membuf_trim(struct membuf *sb, int pos)
+{
+ if(pos < 0 || pos > sb->len)
+ {
+ return -1;
+ }
+ if(pos == 0)
+ {
+ return sb->len;
+ }
+ if(pos != sb->len)
+ {
+ memmove(sb->buf, (char*)sb->buf + pos, sb->len - pos);
+ }
+ sb->len -= pos;
+ return sb->len;
+}
+
+void *membuf_memcpy(struct membuf *sb, int offset, const void *mem, int len)
+{
+ char *buf;
+ membuf_atleast(sb, offset + len);
+ buf = (char*)sb->buf + offset;
+ memcpy(buf, mem, len);
+ return buf;
+}
+
+void *membuf_append(struct membuf *sb, const void *mem, int len)
+{
+ int newlen;
+ void *p;
+ newlen = sb->len + len;
+ membuf_atleast(sb, newlen);
+ p = (char *) sb->buf + sb->len;
+ if(mem == NULL)
+ {
+ memset(p, 0, len);
+ }
+ else
+ {
+ memcpy(p, mem, len);
+ }
+ sb->len = newlen;
+ return p;
+}
+
+void *membuf_append_char(struct membuf *sb, char c)
+{
+ int newlen;
+ char *p;
+ newlen = sb->len + 1;
+ membuf_atleast(sb, newlen);
+ p = (char *) sb->buf + sb->len;
+ *p = c;
+ sb->len = newlen;
+ return p;
+}
+
+void *membuf_insert(struct membuf *sb, int offset, const void *mem, int len)
+{
+ int newlen;
+ void *from;
+ void *to;
+ newlen = sb->len + len;
+ membuf_atleast(sb, newlen);
+ from = (char *)sb->buf + offset;
+ to = (char *)from + len;
+ memmove(to, from, sb->len - offset);
+ if(mem == NULL)
+ {
+ memset(from, 0, len);
+ }
+ else
+ {
+ memcpy(from, mem, len);
+ }
+ sb->len = newlen;
+ return from;
+}
+
+void membuf_remove(struct membuf *sb, int offset, int len)
+{
+ void *from;
+ void *to;
+ to = (char *)sb->buf + offset;
+ from = (char *)to + len;
+ sb->len -= len;
+ memmove(to, from, sb->len - offset);
+
+}
+
+void membuf_atleast(struct membuf *sb, int len)
+{
+ int size;
+
+ size = sb->size;
+ if (size == 0)
+ size = 1;
+ while (size < len)
+ {
+ size <<= 1;
+ }
+ if (size > sb->size)
+ {
+ sb->buf = realloc(sb->buf, size);
+ if (sb->buf == NULL)
+ {
+ fprintf(stderr, "error, can't reallocate memory\n");
+ exit(1);
+ }
+ sb->size = size;
+ }
+}
+
+void membuf_atmost(struct membuf *sb, int len)
+{
+ int size;
+
+ size = sb->size;
+ while (size > len)
+ {
+ size >>= 1;
+ }
+ if (size < sb->size)
+ {
+ sb->buf = realloc(sb->buf, size);
+ if (sb->buf == NULL)
+ {
+ fprintf(stderr, "error, can't reallocate memory\n");
+ exit(1);
+ }
+ sb->size = size;
+ sb->len = size;
+ }
+}
+
+int membuf_get_size(const struct membuf *sb)
+{
+ return sb->size;
+}
+void *membuf_get(const struct membuf *sb)
+{
+ return sb->buf;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+struct _interval_node {
+ int start;
+ int score;
+ struct _interval_node *next;
+ signed char prefix;
+ signed char bits;
+ signed char depth;
+ signed char flags;
+};
+
+typedef struct _interval_node interval_node[1];
+typedef struct _interval_node *interval_nodep;
+
+static
+void
+interval_node_init(interval_nodep 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
+interval_nodep interval_node_clone(interval_nodep inp)
+{
+ interval_nodep inp2 = NULL;
+
+ if(inp != NULL)
+ {
+ inp2 = malloc(sizeof(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(interval_nodep inp)
+{
+ interval_nodep inp2;
+ while (inp != NULL)
+ {
+ inp2 = inp;
+ inp = inp->next;
+ free(inp2);
+ }
+}
+
+static
+void interval_node_dump(int level, interval_nodep 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, output_ctxp out)
+{
+ interval_nodep inp;
+ int end;
+
+ float val;
+
+ inp = (interval_nodep) 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);
+ } else
+ {
+ val += (float) (arg - end);
+ }
+ 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_matchp mp, encode_match_data emd)
+{
+ interval_nodep *offset;
+ float bits;
+ encode_match_privp data;
+
+ data = emd->priv;
+ offset = data->offset_f_priv;
+
+ bits = 0.0;
+ if (mp->offset == 0)
+ {
+ bits += 9.0f * mp->len;
+ data->lit_num += mp->len;
+ data->lit_bits += bits;
+ } else
+ {
+ bits += 1.0;
+ switch (mp->len)
+ {
+ case 0:
+ LOG(LOG_ERROR, ("bad len\n"));
+ exit(1);
+ break;
+ case 1:
+ bits += data->offset_f(mp->offset, offset[0], emd->out);
+ break;
+ case 2:
+ bits += data->offset_f(mp->offset, offset[1], emd->out);
+ break;
+ default:
+ bits += data->offset_f(mp->offset, offset[7], emd->out);
+ break;
+ }
+ bits += data->len_f(mp->len, data->len_f_priv, emd->out);
+ 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;
+ }
+ }
+ }
+ return bits;
+}
+
+struct _optimize_arg {
+ radix_root cache;
+ int *stats;
+ int *stats2;
+ int max_depth;
+ int flags;
+ struct chunkpool in_pool[1];
+};
+
+#define CACHE_KEY(START, DEPTH, MAXDEPTH) ((int)((START)*(MAXDEPTH)|DEPTH))
+
+typedef struct _optimize_arg optimize_arg[1];
+typedef struct _optimize_arg optimize_argp;
+
+static interval_nodep
+optimize1(optimize_arg arg, int start, int depth, int init)
+{
+ interval_node inp;
+ interval_nodep 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, arg->max_depth);
+ best_inp = radix_node_get(arg->cache, key);
+ if (best_inp != NULL)
+ {
+ break;
+ }
+
+ interval_node_init(inp, start, depth, arg->flags);
+
+ for (i = 0; i < 16; ++i)
+ {
+ inp->next = NULL;
+ inp->bits = i;
+ end = start + (1 << i);
+
+ start_count = end_count = 0;
+ if (start < 65536)
+ {
+ start_count = arg->stats[start];
+ if (end < 65536)
+ {
+ end_count = arg->stats[end];
+ }
+ }
+
+ inp->score = (start_count - end_count) *
+ (inp->prefix + inp->bits);
+
+ /* one index below */
+ LOG(LOG_DUMP, ("interval score: [%d«%d[%d\n",
+ start, i, inp->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 */
+ inp->next = optimize1(arg, end, depth + 1, i);
+ }
+ /* get the penalty for skipping */
+ penalty = 100000000;
+ if (arg->stats2 != NULL)
+ {
+ penalty = arg->stats2[end];
+ }
+ if (inp->next != NULL && inp->next->score < penalty)
+ {
+ penalty = inp->next->score;
+ }
+ inp->score += penalty;
+ }
+ if (best_inp == NULL || inp->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 = *inp;
+ }
+ }
+ 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 interval_nodep
+exo_optimize(int stats[65536], int stats2[65536], int max_depth, int flags)
+{
+ optimize_arg arg;
+
+ interval_nodep inp;
+
+ arg->stats = stats;
+ arg->stats2 = stats2;
+
+ arg->max_depth = max_depth;
+ arg->flags = flags;
+
+ chunkpool_init(arg->in_pool, sizeof(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 const char *export_helper(interval_nodep np, int depth)
+{
+ static char buf[20];
+ char *p = buf;
+ while(np != NULL)
+ {
+ p += sprintf(p, "%X", np->bits);
+ np = np->next;
+ --depth;
+ }
+ while(depth-- > 0)
+ {
+ p += sprintf(p, "0");
+ }
+ return buf;
+}
+
+const char *optimal_encoding_export(encode_match_data emd)
+{
+ interval_nodep *offsets;
+ static char buf[100];
+ char *p = buf;
+ encode_match_privp data;
+ data = emd->priv;
+ offsets = (interval_nodep*)data->offset_f_priv;
+ p += sprintf(p, "%s", export_helper((interval_nodep)data->len_f_priv, 16));
+ p += sprintf(p, ",%s", export_helper(offsets[0], 4));
+ p += sprintf(p, ",%s", export_helper(offsets[1], 16));
+ p += sprintf(p, ",%s", export_helper(offsets[7], 16));
+ return buf;
+}
+
+static void import_helper(interval_nodep *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;
+ interval_nodep np;
+
+ if(c == ',')
+ {
+ break;
+ }
+
+ buf[0] = c;
+ bits = strtol(buf, &dummy, 16);
+
+ LOG(LOG_DUMP, ("got bits %d\n", bits));
+
+ np = malloc(sizeof(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(encode_match_data emd,
+ const char *encoding)
+{
+ encode_match_privp data;
+ interval_nodep *npp, *offsets;
+
+ LOG(LOG_DEBUG, ("importing encoding: %s\n", encoding));
+
+ optimal_free(emd);
+ optimal_init(emd);
+
+ data = emd->priv;
+ offsets = (interval_nodep*)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);
+
+ /* offsets, len >= 3 */
+ npp = &offsets[7];
+ import_helper(npp, &encoding, 4);
+
+ LOG(LOG_DEBUG, ("imported encoding: "));
+ optimal_dump(LOG_DEBUG, emd);
+}
+
+void optimal_init(encode_match_data emd) /* IN/OUT */
+{
+ encode_match_privp data;
+ interval_nodep *inpp;
+
+ emd->priv = malloc(sizeof(encode_match_priv));
+ data = emd->priv;
+
+ memset(data, 0, sizeof(encode_match_priv));
+
+ data->offset_f = optimal_encode_int;
+ data->len_f = optimal_encode_int;
+ inpp = malloc(sizeof(interval_nodep[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;
+}
+
+void optimal_free(encode_match_data emd) /* IN */
+{
+ encode_match_privp data;
+ interval_nodep *inpp;
+ interval_nodep 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[65536])
+{
+ 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[65536])
+{
+ int i;
+ for (i = 0; i < 32; ++i)
+ {
+ LOG(level, ("%d, ", arr[i]));
+ }
+ LOG(level, ("\n"));
+}
+
+void optimal_optimize(encode_match_data emd, /* IN/OUT */
+ matchp_enum_get_next_f * f, /* IN */
+ void *matchp_enum) /* IN */
+{
+ encode_match_privp data;
+ const_matchp mp;
+ interval_nodep *offset;
+ static int offset_arr[8][65536];
+ static int offset_parr[8][65536];
+ static int len_arr[65536];
+ int treshold;
+
+ int i, j;
+ void *priv1;
+
+ 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 */
+ priv1 = matchp_enum;
+#if 0
+ while ((mp = f(priv1)) != NULL)
+ {
+ LOG(LOG_DEBUG, ("%p len %d offset %d\n", mp, mp->len, mp->offset));
+ }
+ if(mp->len < 0)
+ {
+ LOG(LOG_ERROR, ("the horror, negative len!\n"));
+ }
+#endif
+ while ((mp = f(priv1)) != NULL && mp->len > 0)
+ {
+ 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 = exo_optimize(len_arr, NULL, 16, -1);
+
+ /* then the offsets */
+ priv1 = matchp_enum;
+ while ((mp = f(priv1)) != NULL && mp->len > 0)
+ {
+ if (mp->offset > 0)
+ {
+ treshold = mp->len * 9;
+ treshold -= 1 + (int) optimal_encode_int(mp->len,
+ data->len_f_priv,
+ 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;
+ 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 = 65534; 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] = exo_optimize(offset_arr[0], offset_parr[0], 1 << 2, 2);
+ offset[1] = exo_optimize(offset_arr[1], offset_parr[1], 1 << 4, 4);
+ offset[2] = exo_optimize(offset_arr[2], offset_parr[2], 1 << 4, 4);
+ offset[3] = exo_optimize(offset_arr[3], offset_parr[3], 1 << 4, 4);
+ offset[4] = exo_optimize(offset_arr[4], offset_parr[4], 1 << 4, 4);
+ offset[5] = exo_optimize(offset_arr[5], offset_parr[5], 1 << 4, 4);
+ offset[6] = exo_optimize(offset_arr[6], offset_parr[6], 1 << 4, 4);
+ offset[7] = exo_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, encode_match_data emd)
+{
+ encode_match_privp data;
+ interval_nodep *offset;
+ interval_nodep 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]);
+
+ LOG(level, ("offsets (len =8): "));
+ interval_node_dump(level, offset[7]);
+}
+
+static
+void interval_out(output_ctx out, interval_nodep inp1, int size)
+{
+ unsigned char buffer[256];
+ unsigned char count;
+ interval_nodep 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));
+ output_bits(out, 4, b);
+ size--;
+ }
+}
+
+void optimal_out(output_ctx out, /* IN/OUT */
+ encode_match_data emd) /* IN */
+{
+ encode_match_privp data;
+ interval_nodep *offset;
+ interval_nodep len;
+
+ data = emd->priv;
+
+ offset = data->offset_f_priv;
+ len = data->len_f_priv;
+
+ interval_out(out, offset[0], 4);
+ interval_out(out, offset[1], 16);
+ interval_out(out, offset[7], 16);
+ interval_out(out, len, 16);
+}
+
+void output_ctx_init(output_ctx ctx, struct membuf *out) /* IN/OUT */
+{
+ ctx->bitbuf = 1;
+ ctx->pos = membuf_memlen(out);
+ ctx->buf = out;
+}
+
+unsigned int output_get_pos(output_ctx ctx) /* IN */
+{
+ return ctx->pos;
+}
+
+void output_byte(output_ctx ctx, /* IN/OUT */
+ unsigned char byte) /* IN */
+{
+ /*LOG(LOG_DUMP, ("output_byte: $%02X\n", byte)); */
+ if(ctx->pos < membuf_memlen(ctx->buf))
+ {
+ char *p;
+ p = membuf_get(ctx->buf);
+ p[ctx->pos] = byte;
+ }
+ else
+ {
+ while(ctx->pos > membuf_memlen(ctx->buf))
+ {
+ membuf_append_char(ctx->buf, '\0');
+ }
+ membuf_append_char(ctx->buf, byte);
+ }
+ ++(ctx->pos);
+}
+
+void output_word(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(output_ctx ctx) /* IN/OUT */
+{
+ /* flush the bitbuf including
+ * the extra 1 bit acting as eob flag */
+ output_byte(ctx, (unsigned char) (ctx->bitbuf & 0xFF));
+ if (ctx->bitbuf & 0x100)
+ {
+ output_byte(ctx, 1);
+ }
+ LOG(LOG_DUMP, ("bitstream flushed 0x%02X\n", ctx->bitbuf & 0xFF));
+
+ /* reset it */
+ ctx->bitbuf = 1;
+}
+
+void bits_dump(int count, int val)
+{
+ static char buf[1024];
+ char *pek;
+ pek = buf;
+ if (count > 0)
+ {
+ pek += sprintf(pek, "0x%04X, % 2d: ", val, count);
+ }
+ while (count-- > 0)
+ {
+ *(pek++) = val & (1 << count) ? '1' : '0';
+ }
+ *(pek++) = '\0';
+ LOG(LOG_NORMAL, ("%s\n", buf));
+}
+
+static void output_bits_int(output_ctx ctx, /* IN/OUT */
+ int count, /* IN */
+ int val) /* IN */
+{
+ /* this makes the bits appear in reversed
+ * big endian order in the output stream */
+ while (count-- > 0)
+ {
+ ctx->bitbuf <<= 1;
+ ctx->bitbuf |= val & 0x1;
+ val >>= 1;
+ if (ctx->bitbuf & 0x100)
+ {
+ /* full byte, flush it */
+ output_byte(ctx, (unsigned char) (ctx->bitbuf & 0xFF));
+ LOG(LOG_DUMP,
+ ("bitstream byte 0x%02X\n", ctx->bitbuf & 0xFF));
+ ctx->bitbuf = 1;
+ }
+ }
+}
+
+void output_bits(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(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);
+ }
+}
+
+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"
+
+unsigned char *Exomizer_crunch(unsigned char *input_data, int input_len, int *retlen)
+{
+ int argc=1;
+ char **argv=NULL;
+ char flags_arr[32];
+ int decrunch_mode = 0;
+ int backwards_mode = 0;
+ int reverse_mode = 0;
+ int c, infilec;
+ char **infilev;
+ /* output buffer */
+ unsigned char *output_data;
+
+ static struct crunch_options options[1] = { CRUNCH_OPTIONS_DEFAULT };
+ struct common_flags flags[1] = { {options, DEFAULT_OUTFILE} };
+
+ struct membuf inbuf[1];
+ struct membuf outbuf[1];
+
+ const char *appl;;
+
+
+ argv=malloc(sizeof(char *));
+ argv[0]=strdup("mem_exomizer");
+ /* init args */
+ appl = fixup_appl(argv[0]);
+
+ sprintf(flags_arr, "bdr%s", CRUNCH_FLAGS);
+ while ((c = getflag(argc, argv, flags_arr)) != -1)
+ {
+ switch (c)
+ {
+ case 'b':
+ backwards_mode = 1;
+ break;
+ case 'r':
+ reverse_mode = 1;
+ break;
+ default:
+ handle_crunch_flags(c, flagarg, print_usage, appl, flags);
+ }
+ }
+
+ infilev = argv + flagind;
+ infilec = argc - flagind;
+
+
+printf("crunching with exomizer (the art of patience...)\n");
+
+/* only memory */
+
+ membuf_init(inbuf);
+ membuf_init(outbuf);
+
+ /* rustine */
+ membuf_append(inbuf, input_data, input_len);
+
+
+ {
+ struct crunch_info info[1];
+ if(backwards_mode)
+ {
+ crunch_backwards(inbuf, outbuf, options, info);
+ }
+ else
+ {
+ exocrunch(inbuf, outbuf, options, info);
+ }
+ }
+
+ if(reverse_mode)
+ {
+ reverse_buffer(membuf_get(outbuf), membuf_memlen(outbuf));
+ }
+
+ output_data=MemMalloc(membuf_memlen(outbuf));
+ memcpy(output_data,membuf_get(outbuf),membuf_memlen(outbuf));
+ *retlen=membuf_memlen(outbuf);
+
+ membuf_free(outbuf);
+ membuf_free(inbuf);
+
+ return output_data;
+}
diff --git a/tools/rasm/lz4.h b/tools/rasm/lz4.h
new file mode 100644
index 0000000..f34bae4
--- /dev/null
+++ b/tools/rasm/lz4.h
@@ -0,0 +1,3329 @@
+/*
+
+Warning! This is a modified version of original sources!
+
+To sum up:
+- all include files and C sources were merged in a single file
+- existing logs were removed (except error logs)
+- main were removed and wrapper added
+
+
+ * LZ4 - Fast LZ compression algorithm
+ * Header File
+ * Copyright (C) 2011-2017, Yann Collet.
+
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ 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.
+
+ 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
+ OWNER 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.
+
+ You can contact the author at :
+ - LZ4 homepage : http://www.lz4.org
+ - LZ4 source repository : https://github.com/lz4/lz4
+*/
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#ifndef LZ4_H_2983827168210
+#define LZ4_H_2983827168210
+
+/* --- Dependency --- */
+
+
+/**
+ Introduction
+
+ LZ4 is lossless compression algorithm, providing compression speed at 400 MB/s per core,
+ scalable with multi-cores CPU. It features an extremely fast decoder, with speed in
+ multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.
+
+ The LZ4 compression library provides in-memory compression and decompression functions.
+ Compression can be done in:
+ - a single step (described as Simple Functions)
+ - a single step, reusing a context (described in Advanced Functions)
+ - unbounded multiple steps (described as Streaming compression)
+
+ lz4.h provides block compression functions. It gives full buffer control to user.
+ Decompressing an lz4-compressed block also requires metadata (such as compressed size).
+ Each application is free to encode such metadata in whichever way it wants.
+
+ An additional format, called LZ4 frame specification (doc/lz4_Frame_format.md),
+ take care of encoding standard metadata alongside LZ4-compressed blocks.
+ If your application requires interoperability, it's recommended to use it.
+ A library is provided to take care of it, see lz4frame.h.
+*/
+
+/*^***************************************************************
+* Export parameters
+*****************************************************************/
+/*
+* LZ4_DLL_EXPORT :
+* Enable exporting of functions when building a Windows DLL
+* LZ4LIB_API :
+* Control library symbols visibility.
+*/
+#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)
+# define LZ4LIB_API __declspec(dllexport)
+#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)
+# define LZ4LIB_API __declspec(dllimport) /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
+#elif defined(__GNUC__) && (__GNUC__ >= 4)
+# define LZ4LIB_API __attribute__ ((__visibility__ ("default")))
+#else
+# define LZ4LIB_API
+#endif
+
+
+/*------ Version ------*/
+#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
+#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */
+#define LZ4_VERSION_RELEASE 6 /* for tweaks, bug-fixes, or development */
+
+#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
+
+#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
+#define LZ4_QUOTE(str) #str
+#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
+#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)
+
+LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; to be used when checking dll version */
+LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; to be used when checking dll version */
+
+
+/*-************************************
+* Tuning parameter
+**************************************/
+/*!
+ * LZ4_MEMORY_USAGE :
+ * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
+ * Increasing memory usage improves compression ratio
+ * Reduced memory usage can improve speed, due to cache effect
+ * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
+ */
+#ifndef LZ4_MEMORY_USAGE
+# define LZ4_MEMORY_USAGE 14
+#endif
+
+/*-************************************
+* Simple Functions
+**************************************/
+/*! LZ4_compress_default() :
+ Compresses 'sourceSize' bytes from buffer 'source'
+ into already allocated 'dest' buffer of size 'maxDestSize'.
+ Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize).
+ It also runs faster, so it's a recommended setting.
+ If the function cannot compress 'source' into a more limited 'dest' budget,
+ compression stops *immediately*, and the function result is zero.
+ As a consequence, 'dest' content is not valid.
+ This function never writes outside 'dest' buffer, nor read outside 'source' buffer.
+ sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE
+ maxDestSize : full or partial size of buffer 'dest' (which must be already allocated)
+ return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize)
+ or 0 if compression fails */
+LZ4LIB_API int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize);
+
+/*! LZ4_decompress_safe() :
+ compressedSize : is the precise full size of the compressed block.
+ maxDecompressedSize : is the size of destination buffer, which must be already allocated.
+ return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize)
+ If destination buffer is not large enough, decoding will stop and output an error code (<0).
+ If the source stream is detected malformed, the function will stop decoding and return a negative result.
+ This function is protected against buffer overflow exploits, including malicious data packets.
+ It never writes outside output buffer, nor reads outside input buffer.
+*/
+LZ4LIB_API int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize);
+
+
+/*-************************************
+* Advanced Functions
+**************************************/
+#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
+#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
+
+/*!
+LZ4_compressBound() :
+ Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
+ This function is primarily useful for memory allocation purposes (destination buffer size).
+ Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
+ Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize)
+ inputSize : max supported value is LZ4_MAX_INPUT_SIZE
+ return : maximum output size in a "worst case" scenario
+ or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE)
+*/
+LZ4LIB_API int LZ4_compressBound(int inputSize);
+
+/*!
+LZ4_compress_fast() :
+ Same as LZ4_compress_default(), but allows to select an "acceleration" factor.
+ The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
+ It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
+ An acceleration value of "1" is the same as regular LZ4_compress_default()
+ Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1.
+*/
+LZ4LIB_API int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration);
+
+
+/*!
+LZ4_compress_fast_extState() :
+ Same compression function, just using an externally allocated memory space to store compression state.
+ Use LZ4_sizeofState() to know how much memory must be allocated,
+ and allocate it on 8-bytes boundaries (using malloc() typically).
+ Then, provide it as 'void* state' to compression function.
+*/
+LZ4LIB_API int LZ4_sizeofState(void);
+LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration);
+
+
+/*!
+LZ4_compress_destSize() :
+ Reverse the logic, by compressing as much data as possible from 'source' buffer
+ into already allocated buffer 'dest' of size 'targetDestSize'.
+ This function either compresses the entire 'source' content into 'dest' if it's large enough,
+ or fill 'dest' buffer completely with as much data as possible from 'source'.
+ *sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'.
+ New value is necessarily <= old value.
+ return : Nb bytes written into 'dest' (necessarily <= targetDestSize)
+ or 0 if compression fails
+*/
+LZ4LIB_API int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize);
+
+
+/*!
+LZ4_decompress_fast() :
+ originalSize : is the original and therefore uncompressed size
+ return : the number of bytes read from the source buffer (in other words, the compressed size)
+ If the source stream is detected malformed, the function will stop decoding and return a negative result.
+ Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes.
+ note : This function fully respect memory boundaries for properly formed compressed data.
+ It is a bit faster than LZ4_decompress_safe().
+ However, it does not provide any protection against intentionally modified data stream (malicious input).
+ Use this function in trusted environment only (data to decode comes from a trusted source).
+*/
+LZ4LIB_API int LZ4_decompress_fast (const char* source, char* dest, int originalSize);
+
+/*!
+LZ4_decompress_safe_partial() :
+ This function decompress a compressed block of size 'compressedSize' at position 'source'
+ into destination buffer 'dest' of size 'maxDecompressedSize'.
+ The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached,
+ reducing decompression time.
+ return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize)
+ Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller.
+ Always control how many bytes were decoded.
+ If the source stream is detected malformed, the function will stop decoding and return a negative result.
+ This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets
+*/
+LZ4LIB_API int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize);
+
+
+/*-*********************************************
+* Streaming Compression Functions
+***********************************************/
+typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
+
+/*! LZ4_createStream() and LZ4_freeStream() :
+ * LZ4_createStream() will allocate and initialize an `LZ4_stream_t` structure.
+ * LZ4_freeStream() releases its memory.
+ */
+LZ4LIB_API LZ4_stream_t* LZ4_createStream(void);
+LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr);
+
+/*! LZ4_resetStream() :
+ * An LZ4_stream_t structure can be allocated once and re-used multiple times.
+ * Use this function to init an allocated `LZ4_stream_t` structure and start a new compression.
+ */
+LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
+
+/*! LZ4_loadDict() :
+ * Use this function to load a static dictionary into LZ4_stream.
+ * Any previous data will be forgotten, only 'dictionary' will remain in memory.
+ * Loading a size of 0 is allowed.
+ * Return : dictionary size, in bytes (necessarily <= 64 KB)
+ */
+LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
+
+/*! LZ4_compress_fast_continue() :
+ * Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio.
+ * Important : Previous data blocks are assumed to remain present and unmodified !
+ * 'dst' buffer must be already allocated.
+ * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
+ * If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero.
+ * After an error, the stream status is invalid, and it can only be reset or freed.
+ */
+LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
+
+/*! LZ4_saveDict() :
+ * If previously compressed data block is not guaranteed to remain available at its current memory location,
+ * save it into a safer place (char* safeBuffer).
+ * Note : it's not necessary to call LZ4_loadDict() after LZ4_saveDict(), dictionary is immediately usable.
+ * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.
+ */
+LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize);
+
+
+/*-**********************************************
+* Streaming Decompression Functions
+* Bufferless synchronous API
+************************************************/
+typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* incomplete type (defined later) */
+
+/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() :
+ * creation / destruction of streaming decompression tracking structure */
+LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);
+LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
+
+/*! LZ4_setStreamDecode() :
+ * Use this function to instruct where to find the dictionary.
+ * Setting a size of 0 is allowed (same effect as reset).
+ * @return : 1 if OK, 0 if error
+ */
+LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
+
+/*!
+LZ4_decompress_*_continue() :
+ These decoding functions allow decompression of multiple blocks in "streaming" mode.
+ Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB)
+ In the case of a ring buffers, decoding buffer must be either :
+ - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions)
+ In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB).
+ - Larger than encoding buffer, by a minimum of maxBlockSize more bytes.
+ maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block.
+ In which case, encoding and decoding buffers do not need to be synchronized,
+ and encoding ring buffer can have any size, including small ones ( < 64 KB).
+ - _At least_ 64 KB + 8 bytes + maxBlockSize.
+ In which case, encoding and decoding buffers do not need to be synchronized,
+ and encoding ring buffer can have any size, including larger than decoding buffer.
+ Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer,
+ and indicate where it is saved using LZ4_setStreamDecode()
+*/
+LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize);
+LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize);
+
+
+/*! LZ4_decompress_*_usingDict() :
+ * These decoding functions work the same as
+ * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue()
+ * They are stand-alone, and don't need an LZ4_streamDecode_t structure.
+ */
+LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize);
+LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize);
+
+
+/*^**********************************************
+ * !!!!!! STATIC LINKING ONLY !!!!!!
+ ***********************************************/
+/*-************************************
+ * Private definitions
+ **************************************
+ * Do not use these definitions.
+ * They are exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.
+ * Using these definitions will expose code to API and/or ABI break in future versions of the library.
+ **************************************/
+#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
+#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
+#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
+
+#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+
+typedef struct {
+ uint32_t hashTable[LZ4_HASH_SIZE_U32];
+ uint32_t currentOffset;
+ uint32_t initCheck;
+ const uint8_t* dictionary;
+ uint8_t* bufferStart; /* obsolete, used for slideInputBuffer */
+ uint32_t dictSize;
+} LZ4_stream_t_internal;
+
+typedef struct {
+ const uint8_t* externalDict;
+ size_t extDictSize;
+ const uint8_t* prefixEnd;
+ size_t prefixSize;
+} LZ4_streamDecode_t_internal;
+
+#else
+
+typedef struct {
+ unsigned int hashTable[LZ4_HASH_SIZE_U32];
+ unsigned int currentOffset;
+ unsigned int initCheck;
+ const unsigned char* dictionary;
+ unsigned char* bufferStart; /* obsolete, used for slideInputBuffer */
+ unsigned int dictSize;
+} LZ4_stream_t_internal;
+
+typedef struct {
+ const unsigned char* externalDict;
+ size_t extDictSize;
+ const unsigned char* prefixEnd;
+ size_t prefixSize;
+} LZ4_streamDecode_t_internal;
+
+#endif
+
+/*!
+ * LZ4_stream_t :
+ * information structure to track an LZ4 stream.
+ * init this structure before first use.
+ * note : only use in association with static linking !
+ * this definition is not API/ABI safe,
+ * and may change in a future version !
+ */
+#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
+#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long))
+union LZ4_stream_u {
+ unsigned long long table[LZ4_STREAMSIZE_U64];
+ LZ4_stream_t_internal internal_donotuse;
+} ; /* previously typedef'd to LZ4_stream_t */
+
+
+/*!
+ * LZ4_streamDecode_t :
+ * information structure to track an LZ4 stream during decompression.
+ * init this structure using LZ4_setStreamDecode (or memset()) before first use
+ * note : only use in association with static linking !
+ * this definition is not API/ABI safe,
+ * and may change in a future version !
+ */
+#define LZ4_STREAMDECODESIZE_U64 4
+#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
+union LZ4_streamDecode_u {
+ unsigned long long table[LZ4_STREAMDECODESIZE_U64];
+ LZ4_streamDecode_t_internal internal_donotuse;
+} ; /* previously typedef'd to LZ4_streamDecode_t */
+
+
+/*-************************************
+* Obsolete Functions
+**************************************/
+
+/*! Deprecation warnings
+ Should deprecation warnings be a problem,
+ it is generally possible to disable them,
+ typically with -Wno-deprecated-declarations for gcc
+ or _CRT_SECURE_NO_WARNINGS in Visual.
+ Otherwise, it's also possible to define LZ4_DISABLE_DEPRECATE_WARNINGS */
+#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS
+# define LZ4_DEPRECATED(message) /* disable deprecation warnings */
+#else
+# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
+# define LZ4_DEPRECATED(message) [[deprecated(message)]]
+# elif (LZ4_GCC_VERSION >= 405) || defined(__clang__)
+# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
+# elif (LZ4_GCC_VERSION >= 301)
+# define LZ4_DEPRECATED(message) __attribute__((deprecated))
+# elif defined(_MSC_VER)
+# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
+# else
+# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler")
+# define LZ4_DEPRECATED(message)
+# endif
+#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */
+
+/* Obsolete compression functions */
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_default() instead") int LZ4_compress (const char* source, char* dest, int sourceSize);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_default() instead") int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
+
+/* Obsolete decompression functions */
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_decompress_fast() instead") int LZ4_uncompress (const char* source, char* dest, int outputSize);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_decompress_safe() instead") int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);
+
+/* Obsolete streaming functions; use new streaming interface whenever possible */
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_createStream() instead") void* LZ4_create (char* inputBuffer);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void* state, char* inputBuffer);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_saveDict() instead") char* LZ4_slideInputBuffer (void* state);
+
+/* Obsolete streaming decoding functions */
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
+
+#endif /* LZ4_H_2983827168210 */
+
+#if defined (__cplusplus)
+}
+#endif
+/*
+ LZ4 - Fast LZ compression algorithm
+ Copyright (C) 2011-2017, Yann Collet.
+
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ 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.
+
+ 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
+ OWNER 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.
+
+ You can contact the author at :
+ - LZ4 homepage : http://www.lz4.org
+ - LZ4 source repository : https://github.com/lz4/lz4
+*/
+
+
+/*-************************************
+* Tuning parameters
+**************************************/
+/*
+ * HEAPMODE :
+ * Select how default compression functions will allocate memory for their hash table,
+ * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).
+ */
+#ifndef HEAPMODE
+# define HEAPMODE 0
+#endif
+
+/*
+ * ACCELERATION_DEFAULT :
+ * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0
+ */
+#define ACCELERATION_DEFAULT 1
+
+
+/*-************************************
+* CPU Feature Detection
+**************************************/
+/* LZ4_FORCE_MEMORY_ACCESS
+ * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
+ * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
+ * The below switch allow to select different access method for improved performance.
+ * Method 0 (default) : use `memcpy()`. Safe and portable.
+ * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).
+ * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
+ * Method 2 : direct access. This method is portable but violate C standard.
+ * It can generate buggy code on targets which assembly generation depends on alignment.
+ * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)
+ * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details.
+ * Prefer these methods in priority order (0 > 1 > 2)
+ */
+#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */
+# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
+# define LZ4_FORCE_MEMORY_ACCESS 2
+# elif defined(__INTEL_COMPILER) || defined(__GNUC__)
+# define LZ4_FORCE_MEMORY_ACCESS 1
+# endif
+#endif
+
+/*
+ * LZ4_FORCE_SW_BITCOUNT
+ * Define this parameter if your target system or compiler does not support hardware bit count
+ */
+#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */
+# define LZ4_FORCE_SW_BITCOUNT
+#endif
+
+
+/*-************************************
+* Dependency
+**************************************/
+/* see also "memory routines" below */
+
+
+/*-************************************
+* Compiler Options
+**************************************/
+#ifdef _MSC_VER /* Visual Studio */
+# define FORCE_INLINE static __forceinline
+# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
+# pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */
+#else
+# if defined(__GNUC__) || defined(__clang__)
+# define FORCE_INLINE static inline __attribute__((always_inline))
+# elif defined(__cplusplus) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+# define FORCE_INLINE static inline
+# else
+# define FORCE_INLINE static
+# endif
+#endif /* _MSC_VER */
+
+#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
+# define expect(expr,value) (__builtin_expect ((expr),(value)) )
+#else
+# define expect(expr,value) (expr)
+#endif
+
+#define likely(expr) expect((expr) != 0, 1)
+#define unlikely(expr) expect((expr) != 0, 0)
+
+
+/*-************************************
+* Memory routines
+**************************************/
+#define ALLOCATOR(n,s) calloc(n,s)
+#define FREEMEM free
+#define MEM_INIT memset
+
+
+/*-************************************
+* Basic Types
+**************************************/
+#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+ typedef uint8_t BYTE;
+ typedef uint16_t U16;
+ typedef uint32_t U32;
+ typedef int32_t S32;
+ typedef uint64_t U64;
+ typedef uintptr_t uptrval;
+#else
+ typedef unsigned char BYTE;
+ typedef unsigned short U16;
+ typedef unsigned int U32;
+ typedef signed int S32;
+ typedef unsigned long long U64;
+ typedef size_t uptrval; /* generally true, except OpenVMS-64 */
+#endif
+
+#if defined(__x86_64__)
+ typedef U64 reg_t; /* 64-bits in x32 mode */
+#else
+ typedef size_t reg_t; /* 32-bits in x32 mode */
+#endif
+
+/*-************************************
+* Reading and writing into memory
+**************************************/
+static unsigned LZ4_isLittleEndian(void)
+{
+ const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */
+ return one.c[0];
+}
+
+
+#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2)
+/* lie to the compiler about data alignment; use with caution */
+
+static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; }
+static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; }
+static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; }
+
+static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }
+static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }
+
+#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1)
+
+/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
+/* currently only defined for gcc and icc */
+typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) unalign;
+
+static U16 LZ4_read16(const void* ptr) { return ((const unalign*)ptr)->u16; }
+static U32 LZ4_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }
+static reg_t LZ4_read_ARCH(const void* ptr) { return ((const unalign*)ptr)->uArch; }
+
+static void LZ4_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; }
+static void LZ4_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; }
+
+#else /* safe and portable access through memcpy() */
+
+static U16 LZ4_read16(const void* memPtr)
+{
+ U16 val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static U32 LZ4_read32(const void* memPtr)
+{
+ U32 val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static reg_t LZ4_read_ARCH(const void* memPtr)
+{
+ reg_t val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static void LZ4_write16(void* memPtr, U16 value)
+{
+ memcpy(memPtr, &value, sizeof(value));
+}
+
+static void LZ4_write32(void* memPtr, U32 value)
+{
+ memcpy(memPtr, &value, sizeof(value));
+}
+
+#endif /* LZ4_FORCE_MEMORY_ACCESS */
+
+
+static U16 LZ4_readLE16(const void* memPtr)
+{
+ if (LZ4_isLittleEndian()) {
+ return LZ4_read16(memPtr);
+ } else {
+ const BYTE* p = (const BYTE*)memPtr;
+ return (U16)((U16)p[0] + (p[1]<<8));
+ }
+}
+
+static void LZ4_writeLE16(void* memPtr, U16 value)
+{
+ if (LZ4_isLittleEndian()) {
+ LZ4_write16(memPtr, value);
+ } else {
+ BYTE* p = (BYTE*)memPtr;
+ p[0] = (BYTE) value;
+ p[1] = (BYTE)(value>>8);
+ }
+}
+
+static void LZ4_copy8(void* dst, const void* src)
+{
+ memcpy(dst,src,8);
+}
+
+/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */
+static void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd)
+{
+ BYTE* d = (BYTE*)dstPtr;
+ const BYTE* s = (const BYTE*)srcPtr;
+ BYTE* const e = (BYTE*)dstEnd;
+
+ do { LZ4_copy8(d,s); d+=8; s+=8; } while (d<e);
+}
+
+
+/*-************************************
+* Common Constants
+**************************************/
+#define MINMATCH 4
+
+#define WILDCOPYLENGTH 8
+#define LASTLITERALS 5
+#define MFLIMIT (WILDCOPYLENGTH+MINMATCH)
+static const int LZ4_minLength = (MFLIMIT+1);
+
+#define KB *(1 <<10)
+#define MB *(1 <<20)
+#define GB *(1U<<30)
+
+#define MAXD_LOG 16
+#define MAX_DISTANCE ((1 << MAXD_LOG) - 1)
+
+#define ML_BITS 4
+#define ML_MASK ((1U<<ML_BITS)-1)
+#define RUN_BITS (8-ML_BITS)
+#define RUN_MASK ((1U<<RUN_BITS)-1)
+
+
+/*-************************************
+* Common Utils
+**************************************/
+#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
+
+
+/*-************************************
+* Common functions
+**************************************/
+static unsigned LZ4_NbCommonBytes (register reg_t val)
+{
+ if (LZ4_isLittleEndian()) {
+ if (sizeof(val)==8) {
+# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ unsigned long r = 0;
+ _BitScanForward64( &r, (U64)val );
+ return (int)(r>>3);
+# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ return (__builtin_ctzll((U64)val) >> 3);
+# else
+ static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 };
+ return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
+# endif
+ } else /* 32 bits */ {
+# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ unsigned long r;
+ _BitScanForward( &r, (U32)val );
+ return (int)(r>>3);
+# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ return (__builtin_ctz((U32)val) >> 3);
+# else
+ static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 };
+ return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
+# endif
+ }
+ } else /* Big Endian CPU */ {
+ if (sizeof(val)==8) {
+# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ unsigned long r = 0;
+ _BitScanReverse64( &r, val );
+ return (unsigned)(r>>3);
+# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ return (__builtin_clzll((U64)val) >> 3);
+# else
+ unsigned r;
+ if (!(val>>32)) { r=4; } else { r=0; val>>=32; }
+ if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
+ r += (!val);
+ return r;
+# endif
+ } else /* 32 bits */ {
+# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ unsigned long r = 0;
+ _BitScanReverse( &r, (unsigned long)val );
+ return (unsigned)(r>>3);
+# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ return (__builtin_clz((U32)val) >> 3);
+# else
+ unsigned r;
+ if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
+ r += (!val);
+ return r;
+# endif
+ }
+ }
+}
+
+#define STEPSIZE sizeof(reg_t)
+static unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)
+{
+ const BYTE* const pStart = pIn;
+
+ while (likely(pIn<pInLimit-(STEPSIZE-1))) {
+ reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
+ if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }
+ pIn += LZ4_NbCommonBytes(diff);
+ return (unsigned)(pIn - pStart);
+ }
+
+ if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }
+ if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }
+ if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++;
+ return (unsigned)(pIn - pStart);
+}
+
+
+#ifndef LZ4_COMMONDEFS_ONLY
+/*-************************************
+* Local Constants
+**************************************/
+static const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));
+static const U32 LZ4_skipTrigger = 6; /* Increase this value ==> compression run slower on incompressible data */
+
+
+/*-************************************
+* Local Structures and types
+**************************************/
+typedef enum {
+ noLimit = 0,notLimited = 0,
+ limitedOutput = 1,
+ limitedDestSize = 2,
+} limitedOutput_directive;
+typedef enum { byPtr, byU32, byU16 } tableType_t;
+
+typedef enum { noDict = 0, withPrefix64k, usingExtDict } dict_directive;
+typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;
+
+typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive;
+typedef enum { full = 0, partial = 1 } earlyEnd_directive;
+
+
+/*-************************************
+* Local Utils
+**************************************/
+int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; }
+const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; }
+int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); }
+int LZ4_sizeofState() { return LZ4_STREAMSIZE; }
+
+
+/*-******************************
+* Compression functions
+********************************/
+static U32 LZ4_hash4(U32 sequence, tableType_t const tableType)
+{
+ if (tableType == byU16)
+ return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));
+ else
+ return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));
+}
+
+static U32 LZ4_hash5(U64 sequence, tableType_t const tableType)
+{
+ static const U64 prime5bytes = 889523592379ULL;
+ static const U64 prime8bytes = 11400714785074694791ULL;
+ const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;
+ if (LZ4_isLittleEndian())
+ return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));
+ else
+ return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));
+}
+
+FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)
+{
+ if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);
+ return LZ4_hash4(LZ4_read32(p), tableType);
+}
+
+static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase)
+{
+ switch (tableType)
+ {
+ case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }
+ case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }
+ case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }
+ }
+}
+
+FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+ U32 const h = LZ4_hashPosition(p, tableType);
+ LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);
+}
+
+static const BYTE* LZ4_getPositionOnHash(U32 h, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+ if (tableType == byPtr) { const BYTE** hashTable = (const BYTE**) tableBase; return hashTable[h]; }
+ if (tableType == byU32) { const U32* const hashTable = (U32*) tableBase; return hashTable[h] + srcBase; }
+ { const U16* const hashTable = (U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */
+}
+
+FORCE_INLINE const BYTE* LZ4_getPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+ U32 const h = LZ4_hashPosition(p, tableType);
+ return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);
+}
+
+
+/** LZ4_compress_generic() :
+ inlined, to ensure branches are decided at compilation time */
+FORCE_INLINE int LZ4_compress_generic(
+ LZ4_stream_t_internal* const cctx,
+ const char* const source,
+ char* const dest,
+ const int inputSize,
+ const int maxOutputSize,
+ const limitedOutput_directive outputLimited,
+ const tableType_t tableType,
+ const dict_directive dict,
+ const dictIssue_directive dictIssue,
+ const U32 acceleration)
+{
+ const BYTE* ip = (const BYTE*) source;
+ const BYTE* base;
+ const BYTE* lowLimit;
+ const BYTE* const lowRefLimit = ip - cctx->dictSize;
+ const BYTE* const dictionary = cctx->dictionary;
+ const BYTE* const dictEnd = dictionary + cctx->dictSize;
+ const ptrdiff_t dictDelta = dictEnd - (const BYTE*)source;
+ const BYTE* anchor = (const BYTE*) source;
+ const BYTE* const iend = ip + inputSize;
+ const BYTE* const mflimit = iend - MFLIMIT;
+ const BYTE* const matchlimit = iend - LASTLITERALS;
+
+ BYTE* op = (BYTE*) dest;
+ BYTE* const olimit = op + maxOutputSize;
+
+ U32 forwardH;
+
+ /* Init conditions */
+ if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported inputSize, too large (or negative) */
+ switch(dict)
+ {
+ case noDict:
+ default:
+ base = (const BYTE*)source;
+ lowLimit = (const BYTE*)source;
+ break;
+ case withPrefix64k:
+ base = (const BYTE*)source - cctx->currentOffset;
+ lowLimit = (const BYTE*)source - cctx->dictSize;
+ break;
+ case usingExtDict:
+ base = (const BYTE*)source - cctx->currentOffset;
+ lowLimit = (const BYTE*)source;
+ break;
+ }
+ if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
+ if (inputSize<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
+
+ /* First Byte */
+ LZ4_putPosition(ip, cctx->hashTable, tableType, base);
+ ip++; forwardH = LZ4_hashPosition(ip, tableType);
+
+ /* Main Loop */
+ for ( ; ; ) {
+ ptrdiff_t refDelta = 0;
+ const BYTE* match;
+ BYTE* token;
+
+ /* Find a match */
+ { const BYTE* forwardIp = ip;
+ unsigned step = 1;
+ unsigned searchMatchNb = acceleration << LZ4_skipTrigger;
+ do {
+ U32 const h = forwardH;
+ ip = forwardIp;
+ forwardIp += step;
+ step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+ if (unlikely(forwardIp > mflimit)) goto _last_literals;
+
+ match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base);
+ if (dict==usingExtDict) {
+ if (match < (const BYTE*)source) {
+ refDelta = dictDelta;
+ lowLimit = dictionary;
+ } else {
+ refDelta = 0;
+ lowLimit = (const BYTE*)source;
+ } }
+ forwardH = LZ4_hashPosition(forwardIp, tableType);
+ LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base);
+
+ } while ( ((dictIssue==dictSmall) ? (match < lowRefLimit) : 0)
+ || ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip))
+ || (LZ4_read32(match+refDelta) != LZ4_read32(ip)) );
+ }
+
+ /* Catch up */
+ while (((ip>anchor) & (match+refDelta > lowLimit)) && (unlikely(ip[-1]==match[refDelta-1]))) { ip--; match--; }
+
+ /* Encode Literals */
+ { unsigned const litLength = (unsigned)(ip - anchor);
+ token = op++;
+ if ((outputLimited) && /* Check output buffer overflow */
+ (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)))
+ return 0;
+ if (litLength >= RUN_MASK) {
+ int len = (int)litLength-RUN_MASK;
+ *token = (RUN_MASK<<ML_BITS);
+ for(; len >= 255 ; len-=255) *op++ = 255;
+ *op++ = (BYTE)len;
+ }
+ else *token = (BYTE)(litLength<<ML_BITS);
+
+ /* Copy Literals */
+ LZ4_wildCopy(op, anchor, op+litLength);
+ op+=litLength;
+ }
+
+_next_match:
+ /* Encode Offset */
+ LZ4_writeLE16(op, (U16)(ip-match)); op+=2;
+
+ /* Encode MatchLength */
+ { unsigned matchCode;
+
+ if ((dict==usingExtDict) && (lowLimit==dictionary)) {
+ const BYTE* limit;
+ match += refDelta;
+ limit = ip + (dictEnd-match);
+ if (limit > matchlimit) limit = matchlimit;
+ matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);
+ ip += MINMATCH + matchCode;
+ if (ip==limit) {
+ unsigned const more = LZ4_count(ip, (const BYTE*)source, matchlimit);
+ matchCode += more;
+ ip += more;
+ }
+ } else {
+ matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
+ ip += MINMATCH + matchCode;
+ }
+
+ if ( outputLimited && /* Check output buffer overflow */
+ (unlikely(op + (1 + LASTLITERALS) + (matchCode>>8) > olimit)) )
+ return 0;
+ if (matchCode >= ML_MASK) {
+ *token += ML_MASK;
+ matchCode -= ML_MASK;
+ LZ4_write32(op, 0xFFFFFFFF);
+ while (matchCode >= 4*255) op+=4, LZ4_write32(op, 0xFFFFFFFF), matchCode -= 4*255;
+ op += matchCode / 255;
+ *op++ = (BYTE)(matchCode % 255);
+ } else
+ *token += (BYTE)(matchCode);
+ }
+
+ anchor = ip;
+
+ /* Test end of chunk */
+ if (ip > mflimit) break;
+
+ /* Fill table */
+ LZ4_putPosition(ip-2, cctx->hashTable, tableType, base);
+
+ /* Test next position */
+ match = LZ4_getPosition(ip, cctx->hashTable, tableType, base);
+ if (dict==usingExtDict) {
+ if (match < (const BYTE*)source) {
+ refDelta = dictDelta;
+ lowLimit = dictionary;
+ } else {
+ refDelta = 0;
+ lowLimit = (const BYTE*)source;
+ } }
+ LZ4_putPosition(ip, cctx->hashTable, tableType, base);
+ if ( ((dictIssue==dictSmall) ? (match>=lowRefLimit) : 1)
+ && (match+MAX_DISTANCE>=ip)
+ && (LZ4_read32(match+refDelta)==LZ4_read32(ip)) )
+ { token=op++; *token=0; goto _next_match; }
+
+ /* Prepare next loop */
+ forwardH = LZ4_hashPosition(++ip, tableType);
+ }
+
+_last_literals:
+ /* Encode Last Literals */
+ { size_t const lastRun = (size_t)(iend - anchor);
+ if ( (outputLimited) && /* Check output buffer overflow */
+ ((op - (BYTE*)dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize) )
+ return 0;
+ if (lastRun >= RUN_MASK) {
+ size_t accumulator = lastRun - RUN_MASK;
+ *op++ = RUN_MASK << ML_BITS;
+ for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
+ *op++ = (BYTE) accumulator;
+ } else {
+ *op++ = (BYTE)(lastRun<<ML_BITS);
+ }
+ memcpy(op, anchor, lastRun);
+ op += lastRun;
+ }
+
+ /* End */
+ return (int) (((char*)op)-dest);
+}
+
+
+int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+ LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse;
+ LZ4_resetStream((LZ4_stream_t*)state);
+ if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
+
+ if (maxOutputSize >= LZ4_compressBound(inputSize)) {
+ if (inputSize < LZ4_64Klimit)
+ return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration);
+ else
+ return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, (sizeof(void*)==8) ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+ } else {
+ if (inputSize < LZ4_64Klimit)
+ return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
+ else
+ return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, (sizeof(void*)==8) ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+ }
+}
+
+
+int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+#if (HEAPMODE)
+ void* ctxPtr = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
+#else
+ LZ4_stream_t ctx;
+ void* const ctxPtr = &ctx;
+#endif
+
+ int const result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);
+
+#if (HEAPMODE)
+ FREEMEM(ctxPtr);
+#endif
+ return result;
+}
+
+
+int LZ4_compress_default(const char* source, char* dest, int inputSize, int maxOutputSize)
+{
+ return LZ4_compress_fast(source, dest, inputSize, maxOutputSize, 1);
+}
+
+
+/* hidden debug function */
+/* strangely enough, gcc generates faster code when this function is uncommented, even if unused */
+int LZ4_compress_fast_force(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+ LZ4_stream_t ctx;
+ LZ4_resetStream(&ctx);
+
+ if (inputSize < LZ4_64Klimit)
+ return LZ4_compress_generic(&ctx.internal_donotuse, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
+ else
+ return LZ4_compress_generic(&ctx.internal_donotuse, source, dest, inputSize, maxOutputSize, limitedOutput, sizeof(void*)==8 ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+}
+
+
+/*-******************************
+* *_destSize() variant
+********************************/
+
+static int LZ4_compress_destSize_generic(
+ LZ4_stream_t_internal* const ctx,
+ const char* const src,
+ char* const dst,
+ int* const srcSizePtr,
+ const int targetDstSize,
+ const tableType_t tableType)
+{
+ const BYTE* ip = (const BYTE*) src;
+ const BYTE* base = (const BYTE*) src;
+ const BYTE* lowLimit = (const BYTE*) src;
+ const BYTE* anchor = ip;
+ const BYTE* const iend = ip + *srcSizePtr;
+ const BYTE* const mflimit = iend - MFLIMIT;
+ const BYTE* const matchlimit = iend - LASTLITERALS;
+
+ BYTE* op = (BYTE*) dst;
+ BYTE* const oend = op + targetDstSize;
+ BYTE* const oMaxLit = op + targetDstSize - 2 /* offset */ - 8 /* because 8+MINMATCH==MFLIMIT */ - 1 /* token */;
+ BYTE* const oMaxMatch = op + targetDstSize - (LASTLITERALS + 1 /* token */);
+ BYTE* const oMaxSeq = oMaxLit - 1 /* token */;
+
+ U32 forwardH;
+
+
+ /* Init conditions */
+ if (targetDstSize < 1) return 0; /* Impossible to store anything */
+ if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */
+ if ((tableType == byU16) && (*srcSizePtr>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
+ if (*srcSizePtr<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
+
+ /* First Byte */
+ *srcSizePtr = 0;
+ LZ4_putPosition(ip, ctx->hashTable, tableType, base);
+ ip++; forwardH = LZ4_hashPosition(ip, tableType);
+
+ /* Main Loop */
+ for ( ; ; ) {
+ const BYTE* match;
+ BYTE* token;
+
+ /* Find a match */
+ { const BYTE* forwardIp = ip;
+ unsigned step = 1;
+ unsigned searchMatchNb = 1 << LZ4_skipTrigger;
+
+ do {
+ U32 h = forwardH;
+ ip = forwardIp;
+ forwardIp += step;
+ step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+ if (unlikely(forwardIp > mflimit)) goto _last_literals;
+
+ match = LZ4_getPositionOnHash(h, ctx->hashTable, tableType, base);
+ forwardH = LZ4_hashPosition(forwardIp, tableType);
+ LZ4_putPositionOnHash(ip, h, ctx->hashTable, tableType, base);
+
+ } while ( ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip))
+ || (LZ4_read32(match) != LZ4_read32(ip)) );
+ }
+
+ /* Catch up */
+ while ((ip>anchor) && (match > lowLimit) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }
+
+ /* Encode Literal length */
+ { unsigned litLength = (unsigned)(ip - anchor);
+ token = op++;
+ if (op + ((litLength+240)/255) + litLength > oMaxLit) {
+ /* Not enough space for a last match */
+ op--;
+ goto _last_literals;
+ }
+ if (litLength>=RUN_MASK) {
+ unsigned len = litLength - RUN_MASK;
+ *token=(RUN_MASK<<ML_BITS);
+ for(; len >= 255 ; len-=255) *op++ = 255;
+ *op++ = (BYTE)len;
+ }
+ else *token = (BYTE)(litLength<<ML_BITS);
+
+ /* Copy Literals */
+ LZ4_wildCopy(op, anchor, op+litLength);
+ op += litLength;
+ }
+
+_next_match:
+ /* Encode Offset */
+ LZ4_writeLE16(op, (U16)(ip-match)); op+=2;
+
+ /* Encode MatchLength */
+ { size_t matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
+
+ if (op + ((matchLength+240)/255) > oMaxMatch) {
+ /* Match description too long : reduce it */
+ matchLength = (15-1) + (oMaxMatch-op) * 255;
+ }
+ ip += MINMATCH + matchLength;
+
+ if (matchLength>=ML_MASK) {
+ *token += ML_MASK;
+ matchLength -= ML_MASK;
+ while (matchLength >= 255) { matchLength-=255; *op++ = 255; }
+ *op++ = (BYTE)matchLength;
+ }
+ else *token += (BYTE)(matchLength);
+ }
+
+ anchor = ip;
+
+ /* Test end of block */
+ if (ip > mflimit) break;
+ if (op > oMaxSeq) break;
+
+ /* Fill table */
+ LZ4_putPosition(ip-2, ctx->hashTable, tableType, base);
+
+ /* Test next position */
+ match = LZ4_getPosition(ip, ctx->hashTable, tableType, base);
+ LZ4_putPosition(ip, ctx->hashTable, tableType, base);
+ if ( (match+MAX_DISTANCE>=ip)
+ && (LZ4_read32(match)==LZ4_read32(ip)) )
+ { token=op++; *token=0; goto _next_match; }
+
+ /* Prepare next loop */
+ forwardH = LZ4_hashPosition(++ip, tableType);
+ }
+
+_last_literals:
+ /* Encode Last Literals */
+ { size_t lastRunSize = (size_t)(iend - anchor);
+ if (op + 1 /* token */ + ((lastRunSize+240)/255) /* litLength */ + lastRunSize /* literals */ > oend) {
+ /* adapt lastRunSize to fill 'dst' */
+ lastRunSize = (oend-op) - 1;
+ lastRunSize -= (lastRunSize+240)/255;
+ }
+ ip = anchor + lastRunSize;
+
+ if (lastRunSize >= RUN_MASK) {
+ size_t accumulator = lastRunSize - RUN_MASK;
+ *op++ = RUN_MASK << ML_BITS;
+ for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
+ *op++ = (BYTE) accumulator;
+ } else {
+ *op++ = (BYTE)(lastRunSize<<ML_BITS);
+ }
+ memcpy(op, anchor, lastRunSize);
+ op += lastRunSize;
+ }
+
+ /* End */
+ *srcSizePtr = (int) (((const char*)ip)-src);
+ return (int) (((char*)op)-dst);
+}
+
+
+static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize)
+{
+ LZ4_resetStream(state);
+
+ if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */
+ return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);
+ } else {
+ if (*srcSizePtr < LZ4_64Klimit)
+ return LZ4_compress_destSize_generic(&state->internal_donotuse, src, dst, srcSizePtr, targetDstSize, byU16);
+ else
+ return LZ4_compress_destSize_generic(&state->internal_donotuse, src, dst, srcSizePtr, targetDstSize, sizeof(void*)==8 ? byU32 : byPtr);
+ }
+}
+
+
+int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)
+{
+#if (HEAPMODE)
+ LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
+#else
+ LZ4_stream_t ctxBody;
+ LZ4_stream_t* ctx = &ctxBody;
+#endif
+
+ int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);
+
+#if (HEAPMODE)
+ FREEMEM(ctx);
+#endif
+ return result;
+}
+
+
+
+/*-******************************
+* Streaming functions
+********************************/
+
+LZ4_stream_t* LZ4_createStream(void)
+{
+ LZ4_stream_t* lz4s = (LZ4_stream_t*)ALLOCATOR(8, LZ4_STREAMSIZE_U64);
+ LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */
+ LZ4_resetStream(lz4s);
+ return lz4s;
+}
+
+void LZ4_resetStream (LZ4_stream_t* LZ4_stream)
+{
+ MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t));
+}
+
+int LZ4_freeStream (LZ4_stream_t* LZ4_stream)
+{
+ FREEMEM(LZ4_stream);
+ return (0);
+}
+
+
+#define HASH_UNIT sizeof(reg_t)
+int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)
+{
+ LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse;
+ const BYTE* p = (const BYTE*)dictionary;
+ const BYTE* const dictEnd = p + dictSize;
+ const BYTE* base;
+
+ if ((dict->initCheck) || (dict->currentOffset > 1 GB)) /* Uninitialized structure, or reuse overflow */
+ LZ4_resetStream(LZ4_dict);
+
+ if (dictSize < (int)HASH_UNIT) {
+ dict->dictionary = NULL;
+ dict->dictSize = 0;
+ return 0;
+ }
+
+ if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;
+ dict->currentOffset += 64 KB;
+ base = p - dict->currentOffset;
+ dict->dictionary = p;
+ dict->dictSize = (U32)(dictEnd - p);
+ dict->currentOffset += dict->dictSize;
+
+ while (p <= dictEnd-HASH_UNIT) {
+ LZ4_putPosition(p, dict->hashTable, byU32, base);
+ p+=3;
+ }
+
+ return dict->dictSize;
+}
+
+
+static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, const BYTE* src)
+{
+ if ((LZ4_dict->currentOffset > 0x80000000) ||
+ ((uptrval)LZ4_dict->currentOffset > (uptrval)src)) { /* address space overflow */
+ /* rescale hash table */
+ U32 const delta = LZ4_dict->currentOffset - 64 KB;
+ const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;
+ int i;
+ for (i=0; i<LZ4_HASH_SIZE_U32; i++) {
+ if (LZ4_dict->hashTable[i] < delta) LZ4_dict->hashTable[i]=0;
+ else LZ4_dict->hashTable[i] -= delta;
+ }
+ LZ4_dict->currentOffset = 64 KB;
+ if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB;
+ LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;
+ }
+}
+
+
+int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+ LZ4_stream_t_internal* streamPtr = &LZ4_stream->internal_donotuse;
+ const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize;
+
+ const BYTE* smallest = (const BYTE*) source;
+ if (streamPtr->initCheck) return 0; /* Uninitialized structure detected */
+ if ((streamPtr->dictSize>0) && (smallest>dictEnd)) smallest = dictEnd;
+ LZ4_renormDictT(streamPtr, smallest);
+ if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
+
+ /* Check overlapping input/dictionary space */
+ { const BYTE* sourceEnd = (const BYTE*) source + inputSize;
+ if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) {
+ streamPtr->dictSize = (U32)(dictEnd - sourceEnd);
+ if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB;
+ if (streamPtr->dictSize < 4) streamPtr->dictSize = 0;
+ streamPtr->dictionary = dictEnd - streamPtr->dictSize;
+ }
+ }
+
+ /* prefix mode : source data follows dictionary */
+ if (dictEnd == (const BYTE*)source) {
+ int result;
+ if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, dictSmall, acceleration);
+ else
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, noDictIssue, acceleration);
+ streamPtr->dictSize += (U32)inputSize;
+ streamPtr->currentOffset += (U32)inputSize;
+ return result;
+ }
+
+ /* external dictionary mode */
+ { int result;
+ if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, dictSmall, acceleration);
+ else
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, noDictIssue, acceleration);
+ streamPtr->dictionary = (const BYTE*)source;
+ streamPtr->dictSize = (U32)inputSize;
+ streamPtr->currentOffset += (U32)inputSize;
+ return result;
+ }
+}
+
+
+/* Hidden debug function, to force external dictionary mode */
+int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int inputSize)
+{
+ LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse;
+ int result;
+ const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize;
+
+ const BYTE* smallest = dictEnd;
+ if (smallest > (const BYTE*) source) smallest = (const BYTE*) source;
+ LZ4_renormDictT(streamPtr, smallest);
+
+ result = LZ4_compress_generic(streamPtr, source, dest, inputSize, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);
+
+ streamPtr->dictionary = (const BYTE*)source;
+ streamPtr->dictSize = (U32)inputSize;
+ streamPtr->currentOffset += (U32)inputSize;
+
+ return result;
+}
+
+
+/*! LZ4_saveDict() :
+ * If previously compressed data block is not guaranteed to remain available at its memory location,
+ * save it into a safer place (char* safeBuffer).
+ * Note : you don't need to call LZ4_loadDict() afterwards,
+ * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue().
+ * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.
+ */
+int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)
+{
+ LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;
+ const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize;
+
+ if ((U32)dictSize > 64 KB) dictSize = 64 KB; /* useless to define a dictionary > 64 KB */
+ if ((U32)dictSize > dict->dictSize) dictSize = dict->dictSize;
+
+ memmove(safeBuffer, previousDictEnd - dictSize, dictSize);
+
+ dict->dictionary = (const BYTE*)safeBuffer;
+ dict->dictSize = (U32)dictSize;
+
+ return dictSize;
+}
+
+
+
+/*-*****************************
+* Decompression functions
+*******************************/
+/*! LZ4_decompress_generic() :
+ * This generic decompression function cover all use cases.
+ * It shall be instantiated several times, using different sets of directives
+ * Note that it is important this generic function is really inlined,
+ * in order to remove useless branches during compilation optimization.
+ */
+FORCE_INLINE int LZ4_decompress_generic(
+ const char* const source,
+ char* const dest,
+ int inputSize,
+ int outputSize, /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */
+
+ int endOnInput, /* endOnOutputSize, endOnInputSize */
+ int partialDecoding, /* full, partial */
+ int targetOutputSize, /* only used if partialDecoding==partial */
+ int dict, /* noDict, withPrefix64k, usingExtDict */
+ const BYTE* const lowPrefix, /* == dest when no prefix */
+ const BYTE* const dictStart, /* only if dict==usingExtDict */
+ const size_t dictSize /* note : = 0 if noDict */
+ )
+{
+ /* Local Variables */
+ const BYTE* ip = (const BYTE*) source;
+ const BYTE* const iend = ip + inputSize;
+
+ BYTE* op = (BYTE*) dest;
+ BYTE* const oend = op + outputSize;
+ BYTE* cpy;
+ BYTE* oexit = op + targetOutputSize;
+ const BYTE* const lowLimit = lowPrefix - dictSize;
+
+ const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize;
+ const unsigned dec32table[] = {0, 1, 2, 1, 4, 4, 4, 4};
+ const int dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3};
+
+ const int safeDecode = (endOnInput==endOnInputSize);
+ const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB)));
+
+
+ /* Special cases */
+ if ((partialDecoding) && (oexit > oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => decode everything */
+ if ((endOnInput) && (unlikely(outputSize==0))) return ((inputSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */
+ if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1);
+
+ /* Main Loop : decode sequences */
+ while (1) {
+ size_t length;
+ const BYTE* match;
+ size_t offset;
+
+ /* get literal length */
+ unsigned const token = *ip++;
+ if ((length=(token>>ML_BITS)) == RUN_MASK) {
+ unsigned s;
+ do {
+ s = *ip++;
+ length += s;
+ } while ( likely(endOnInput ? ip<iend-RUN_MASK : 1) & (s==255) );
+ if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) goto _output_error; /* overflow detection */
+ if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) goto _output_error; /* overflow detection */
+ }
+
+ /* copy literals */
+ cpy = op+length;
+ if ( ((endOnInput) && ((cpy>(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) )
+ || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) )
+ {
+ if (partialDecoding) {
+ if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */
+ if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */
+ } else {
+ if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */
+ if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */
+ }
+ memcpy(op, ip, length);
+ ip += length;
+ op += length;
+ break; /* Necessarily EOF, due to parsing restrictions */
+ }
+ LZ4_wildCopy(op, ip, cpy);
+ ip += length; op = cpy;
+
+ /* get offset */
+ offset = LZ4_readLE16(ip); ip+=2;
+ match = op - offset;
+ if ((checkOffset) && (unlikely(match < lowLimit))) goto _output_error; /* Error : offset outside buffers */
+ LZ4_write32(op, (U32)offset); /* costs ~1%; silence an msan warning when offset==0 */
+
+ /* get matchlength */
+ length = token & ML_MASK;
+ if (length == ML_MASK) {
+ unsigned s;
+ do {
+ s = *ip++;
+ if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error;
+ length += s;
+ } while (s==255);
+ if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */
+ }
+ length += MINMATCH;
+
+ /* check external dictionary */
+ if ((dict==usingExtDict) && (match < lowPrefix)) {
+ if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */
+
+ if (length <= (size_t)(lowPrefix-match)) {
+ /* match can be copied as a single segment from external dictionary */
+ memmove(op, dictEnd - (lowPrefix-match), length);
+ op += length;
+ } else {
+ /* match encompass external dictionary and current block */
+ size_t const copySize = (size_t)(lowPrefix-match);
+ size_t const restSize = length - copySize;
+ memcpy(op, dictEnd - copySize, copySize);
+ op += copySize;
+ if (restSize > (size_t)(op-lowPrefix)) { /* overlap copy */
+ BYTE* const endOfMatch = op + restSize;
+ const BYTE* copyFrom = lowPrefix;
+ while (op < endOfMatch) *op++ = *copyFrom++;
+ } else {
+ memcpy(op, lowPrefix, restSize);
+ op += restSize;
+ } }
+ continue;
+ }
+
+ /* copy match within block */
+ cpy = op + length;
+ if (unlikely(offset<8)) {
+ const int dec64 = dec64table[offset];
+ op[0] = match[0];
+ op[1] = match[1];
+ op[2] = match[2];
+ op[3] = match[3];
+ match += dec32table[offset];
+ memcpy(op+4, match, 4);
+ match -= dec64;
+ } else { LZ4_copy8(op, match); match+=8; }
+ op += 8;
+
+ if (unlikely(cpy>oend-12)) {
+ BYTE* const oCopyLimit = oend-(WILDCOPYLENGTH-1);
+ if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals (uncompressed) */
+ if (op < oCopyLimit) {
+ LZ4_wildCopy(op, match, oCopyLimit);
+ match += oCopyLimit - op;
+ op = oCopyLimit;
+ }
+ while (op<cpy) *op++ = *match++;
+ } else {
+ LZ4_copy8(op, match);
+ if (length>16) LZ4_wildCopy(op+8, match+8, cpy);
+ }
+ op=cpy; /* correction */
+ }
+
+ /* end of decoding */
+ if (endOnInput)
+ return (int) (((char*)op)-dest); /* Nb of output bytes decoded */
+ else
+ return (int) (((const char*)ip)-source); /* Nb of input bytes read */
+
+ /* Overflow error detected */
+_output_error:
+ return (int) (-(((const char*)ip)-source))-1;
+}
+
+
+int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, full, 0, noDict, (BYTE*)dest, NULL, 0);
+}
+
+int LZ4_decompress_safe_partial(const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, partial, targetOutputSize, noDict, (BYTE*)dest, NULL, 0);
+}
+
+int LZ4_decompress_fast(const char* source, char* dest, int originalSize)
+{
+ return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)(dest - 64 KB), NULL, 64 KB);
+}
+
+
+/*===== streaming decompression functions =====*/
+
+/*
+ * If you prefer dynamic allocation methods,
+ * LZ4_createStreamDecode()
+ * provides a pointer (void*) towards an initialized LZ4_streamDecode_t structure.
+ */
+LZ4_streamDecode_t* LZ4_createStreamDecode(void)
+{
+ LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOCATOR(1, sizeof(LZ4_streamDecode_t));
+ return lz4s;
+}
+
+int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream)
+{
+ FREEMEM(LZ4_stream);
+ return 0;
+}
+
+/*!
+ * LZ4_setStreamDecode() :
+ * Use this function to instruct where to find the dictionary.
+ * This function is not necessary if previous data is still available where it was decoded.
+ * Loading a size of 0 is allowed (same effect as no dictionary).
+ * Return : 1 if OK, 0 if error
+ */
+int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize)
+{
+ LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
+ lz4sd->prefixSize = (size_t) dictSize;
+ lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize;
+ lz4sd->externalDict = NULL;
+ lz4sd->extDictSize = 0;
+ return 1;
+}
+
+/*
+*_continue() :
+ These decoding functions allow decompression of multiple blocks in "streaming" mode.
+ Previously decoded blocks must still be available at the memory position where they were decoded.
+ If it's not possible, save the relevant part of decoded data into a safe buffer,
+ and indicate where it stands using LZ4_setStreamDecode()
+*/
+int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)
+{
+ LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
+ int result;
+
+ if (lz4sd->prefixEnd == (BYTE*)dest) {
+ result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+ endOnInputSize, full, 0,
+ usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
+ if (result <= 0) return result;
+ lz4sd->prefixSize += result;
+ lz4sd->prefixEnd += result;
+ } else {
+ lz4sd->extDictSize = lz4sd->prefixSize;
+ lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
+ result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+ endOnInputSize, full, 0,
+ usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize);
+ if (result <= 0) return result;
+ lz4sd->prefixSize = result;
+ lz4sd->prefixEnd = (BYTE*)dest + result;
+ }
+
+ return result;
+}
+
+int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize)
+{
+ LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
+ int result;
+
+ if (lz4sd->prefixEnd == (BYTE*)dest) {
+ result = LZ4_decompress_generic(source, dest, 0, originalSize,
+ endOnOutputSize, full, 0,
+ usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
+ if (result <= 0) return result;
+ lz4sd->prefixSize += originalSize;
+ lz4sd->prefixEnd += originalSize;
+ } else {
+ lz4sd->extDictSize = lz4sd->prefixSize;
+ lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
+ result = LZ4_decompress_generic(source, dest, 0, originalSize,
+ endOnOutputSize, full, 0,
+ usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize);
+ if (result <= 0) return result;
+ lz4sd->prefixSize = originalSize;
+ lz4sd->prefixEnd = (BYTE*)dest + originalSize;
+ }
+
+ return result;
+}
+
+
+/*
+Advanced decoding functions :
+*_usingDict() :
+ These decoding functions work the same as "_continue" ones,
+ the dictionary must be explicitly provided within parameters
+*/
+
+FORCE_INLINE int LZ4_decompress_usingDict_generic(const char* source, char* dest, int compressedSize, int maxOutputSize, int safe, const char* dictStart, int dictSize)
+{
+ if (dictSize==0)
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest, NULL, 0);
+ if (dictStart+dictSize == dest) {
+ if (dictSize >= (int)(64 KB - 1))
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, withPrefix64k, (BYTE*)dest-64 KB, NULL, 0);
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest-dictSize, NULL, 0);
+ }
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize);
+}
+
+int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
+{
+ return LZ4_decompress_usingDict_generic(source, dest, compressedSize, maxOutputSize, 1, dictStart, dictSize);
+}
+
+int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize)
+{
+ return LZ4_decompress_usingDict_generic(source, dest, 0, originalSize, 0, dictStart, dictSize);
+}
+
+/* debug function */
+int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize);
+}
+
+
+/*=*************************************************
+* Obsolete Functions
+***************************************************/
+/* obsolete compression functions */
+int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_default(source, dest, inputSize, maxOutputSize); }
+int LZ4_compress(const char* source, char* dest, int inputSize) { return LZ4_compress_default(source, dest, inputSize, LZ4_compressBound(inputSize)); }
+int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); }
+int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); }
+int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, maxDstSize, 1); }
+int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) { return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); }
+
+/*
+These function names are deprecated and should no longer be used.
+They are only provided here for compatibility with older user programs.
+- LZ4_uncompress is totally equivalent to LZ4_decompress_fast
+- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe
+*/
+int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); }
+int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); }
+
+
+/* Obsolete Streaming functions */
+
+int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; }
+
+static void LZ4_init(LZ4_stream_t* lz4ds, BYTE* base)
+{
+ MEM_INIT(lz4ds, 0, sizeof(LZ4_stream_t));
+ lz4ds->internal_donotuse.bufferStart = base;
+}
+
+int LZ4_resetStreamState(void* state, char* inputBuffer)
+{
+ if ((((uptrval)state) & 3) != 0) return 1; /* Error : pointer is not aligned on 4-bytes boundary */
+ LZ4_init((LZ4_stream_t*)state, (BYTE*)inputBuffer);
+ return 0;
+}
+
+void* LZ4_create (char* inputBuffer)
+{
+ LZ4_stream_t* lz4ds = (LZ4_stream_t*)ALLOCATOR(8, sizeof(LZ4_stream_t));
+ LZ4_init (lz4ds, (BYTE*)inputBuffer);
+ return lz4ds;
+}
+
+char* LZ4_slideInputBuffer (void* LZ4_Data)
+{
+ LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)LZ4_Data)->internal_donotuse;
+ int dictSize = LZ4_saveDict((LZ4_stream_t*)LZ4_Data, (char*)ctx->bufferStart, 64 KB);
+ return (char*)(ctx->bufferStart + dictSize);
+}
+
+/* Obsolete streaming decompression functions */
+
+int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB);
+}
+
+int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize)
+{
+ return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB);
+}
+
+#endif /* LZ4_COMMONDEFS_ONLY */
+/*
+ LZ4 HC - High Compression Mode of LZ4
+ Header File
+ Copyright (C) 2011-2017, Yann Collet.
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ 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.
+
+ 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
+ OWNER 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.
+
+ You can contact the author at :
+ - LZ4 source repository : https://github.com/lz4/lz4
+ - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
+*/
+#ifndef LZ4_HC_H_19834876238432
+#define LZ4_HC_H_19834876238432
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+/* --- Dependency --- */
+/* note : lz4hc is not an independent module, it requires lz4.h/lz4.c for proper compilation */
+
+
+/* --- Useful constants --- */
+#define LZ4HC_CLEVEL_MIN 3
+#define LZ4HC_CLEVEL_DEFAULT 9
+#define LZ4HC_CLEVEL_OPT_MIN 11
+#define LZ4HC_CLEVEL_MAX 12
+
+
+/*-************************************
+ * Block Compression
+ **************************************/
+/*! LZ4_compress_HC() :
+ * Compress data from `src` into `dst`, using the more powerful but slower "HC" algorithm.
+ * `dst` must be already allocated.
+ * Compression is guaranteed to succeed if `dstCapacity >= LZ4_compressBound(srcSize)` (see "lz4.h")
+ * Max supported `srcSize` value is LZ4_MAX_INPUT_SIZE (see "lz4.h")
+ * `compressionLevel` : Recommended values are between 4 and 9, although any value between 1 and LZ4HC_MAX_CLEVEL will work.
+ * Values >LZ4HC_MAX_CLEVEL behave the same as LZ4HC_MAX_CLEVEL.
+ * @return : the number of bytes written into 'dst'
+ * or 0 if compression fails.
+ */
+LZ4LIB_API int LZ4_compress_HC (const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel);
+
+
+/* Note :
+ * Decompression functions are provided within "lz4.h" (BSD license)
+ */
+
+
+/*! LZ4_compress_HC_extStateHC() :
+ * Same as LZ4_compress_HC(), but using an externally allocated memory segment for `state`.
+ * `state` size is provided by LZ4_sizeofStateHC().
+ * Memory segment must be aligned on 8-bytes boundaries (which a normal malloc() will do properly).
+ */
+LZ4LIB_API int LZ4_compress_HC_extStateHC(void* state, const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel);
+LZ4LIB_API int LZ4_sizeofStateHC(void);
+
+
+/*-************************************
+ * Streaming Compression
+ * Bufferless synchronous API
+ **************************************/
+ typedef union LZ4_streamHC_u LZ4_streamHC_t; /* incomplete type (defined later) */
+
+/*! LZ4_createStreamHC() and LZ4_freeStreamHC() :
+ * These functions create and release memory for LZ4 HC streaming state.
+ * Newly created states are automatically initialized.
+ * Existing states can be re-used several times, using LZ4_resetStreamHC().
+ * These methods are API and ABI stable, they can be used in combination with a DLL.
+ */
+LZ4LIB_API LZ4_streamHC_t* LZ4_createStreamHC(void);
+LZ4LIB_API int LZ4_freeStreamHC (LZ4_streamHC_t* streamHCPtr);
+
+LZ4LIB_API void LZ4_resetStreamHC (LZ4_streamHC_t* streamHCPtr, int compressionLevel);
+LZ4LIB_API int LZ4_loadDictHC (LZ4_streamHC_t* streamHCPtr, const char* dictionary, int dictSize);
+
+LZ4LIB_API int LZ4_compress_HC_continue (LZ4_streamHC_t* streamHCPtr, const char* src, char* dst, int srcSize, int maxDstSize);
+
+LZ4LIB_API int LZ4_saveDictHC (LZ4_streamHC_t* streamHCPtr, char* safeBuffer, int maxDictSize);
+
+/*
+ These functions compress data in successive blocks of any size, using previous blocks as dictionary.
+ One key assumption is that previous blocks (up to 64 KB) remain read-accessible while compressing next blocks.
+ There is an exception for ring buffers, which can be smaller than 64 KB.
+ Ring buffers scenario is automatically detected and handled by LZ4_compress_HC_continue().
+
+ Before starting compression, state must be properly initialized, using LZ4_resetStreamHC().
+ A first "fictional block" can then be designated as initial dictionary, using LZ4_loadDictHC() (Optional).
+
+ Then, use LZ4_compress_HC_continue() to compress each successive block.
+ Previous memory blocks (including initial dictionary when present) must remain accessible and unmodified during compression.
+ 'dst' buffer should be sized to handle worst case scenarios (see LZ4_compressBound()), to ensure operation success.
+ Because in case of failure, the API does not guarantee context recovery, and context will have to be reset.
+ If `dst` buffer budget cannot be >= LZ4_compressBound(), consider using LZ4_compress_HC_continue_destSize() instead.
+
+ If, for any reason, previous data block can't be preserved unmodified in memory for next compression block,
+ you can save it to a more stable memory space, using LZ4_saveDictHC().
+ Return value of LZ4_saveDictHC() is the size of dictionary effectively saved into 'safeBuffer'.
+*/
+
+
+ /*-*************************************
+ * PRIVATE DEFINITIONS :
+ * Do not use these definitions.
+ * They are exposed to allow static allocation of `LZ4_streamHC_t`.
+ * Using these definitions makes the code vulnerable to potential API break when upgrading LZ4
+ **************************************/
+#define LZ4HC_DICTIONARY_LOGSIZE 17 /* because of btopt, hc would only need 16 */
+#define LZ4HC_MAXD (1<<LZ4HC_DICTIONARY_LOGSIZE)
+#define LZ4HC_MAXD_MASK (LZ4HC_MAXD - 1)
+
+#define LZ4HC_HASH_LOG 15
+#define LZ4HC_HASHTABLESIZE (1 << LZ4HC_HASH_LOG)
+#define LZ4HC_HASH_MASK (LZ4HC_HASHTABLESIZE - 1)
+
+
+#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+
+typedef struct
+{
+ uint32_t hashTable[LZ4HC_HASHTABLESIZE];
+ uint16_t chainTable[LZ4HC_MAXD];
+ const uint8_t* end; /* next block here to continue on current prefix */
+ const uint8_t* base; /* All index relative to this position */
+ const uint8_t* dictBase; /* alternate base for extDict */
+ uint8_t* inputBuffer; /* deprecated */
+ uint32_t dictLimit; /* below that point, need extDict */
+ uint32_t lowLimit; /* below that point, no more dict */
+ uint32_t nextToUpdate; /* index from which to continue dictionary update */
+ uint32_t searchNum; /* only for optimal parser */
+ uint32_t compressionLevel;
+} LZ4HC_CCtx_internal;
+
+#else
+
+typedef struct
+{
+ unsigned int hashTable[LZ4HC_HASHTABLESIZE];
+ unsigned short chainTable[LZ4HC_MAXD];
+ const unsigned char* end; /* next block here to continue on current prefix */
+ const unsigned char* base; /* All index relative to this position */
+ const unsigned char* dictBase; /* alternate base for extDict */
+ unsigned char* inputBuffer; /* deprecated */
+ unsigned int dictLimit; /* below that point, need extDict */
+ unsigned int lowLimit; /* below that point, no more dict */
+ unsigned int nextToUpdate; /* index from which to continue dictionary update */
+ unsigned int searchNum; /* only for optimal parser */
+ int compressionLevel;
+} LZ4HC_CCtx_internal;
+
+#endif
+
+#define LZ4_STREAMHCSIZE (4*LZ4HC_HASHTABLESIZE + 2*LZ4HC_MAXD + 56) /* 393268 */
+#define LZ4_STREAMHCSIZE_SIZET (LZ4_STREAMHCSIZE / sizeof(size_t))
+union LZ4_streamHC_u {
+ size_t table[LZ4_STREAMHCSIZE_SIZET];
+ LZ4HC_CCtx_internal internal_donotuse;
+}; /* previously typedef'd to LZ4_streamHC_t */
+/*
+ LZ4_streamHC_t :
+ This structure allows static allocation of LZ4 HC streaming state.
+ State must be initialized using LZ4_resetStreamHC() before first use.
+
+ Static allocation shall only be used in combination with static linking.
+ When invoking LZ4 from a DLL, use create/free functions instead, which are API and ABI stable.
+*/
+
+
+/*-************************************
+* Deprecated Functions
+**************************************/
+/* see lz4.h LZ4_DISABLE_DEPRECATE_WARNINGS to turn off deprecation warnings */
+
+/* deprecated compression functions */
+/* these functions will trigger warning messages in future releases */
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC() instead") int LZ4_compressHC (const char* source, char* dest, int inputSize);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC() instead") int LZ4_compressHC_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC() instead") int LZ4_compressHC2 (const char* source, char* dest, int inputSize, int compressionLevel);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC() instead") int LZ4_compressHC2_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") int LZ4_compressHC_withStateHC (void* state, const char* source, char* dest, int inputSize);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") int LZ4_compressHC2_withStateHC (void* state, const char* source, char* dest, int inputSize, int compressionLevel);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") int LZ4_compressHC2_limitedOutput_withStateHC(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") int LZ4_compressHC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
+
+/* Deprecated Streaming functions using older model; should no longer be used */
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_createStreamHC() instead") void* LZ4_createHC (char* inputBuffer);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_saveDictHC() instead") char* LZ4_slideInputBufferHC (void* LZ4HC_Data);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_freeStreamHC() instead") int LZ4_freeHC (void* LZ4HC_Data);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int compressionLevel);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_createStreamHC() instead") int LZ4_sizeofStreamStateHC(void);
+LZ4LIB_API LZ4_DEPRECATED("use LZ4_resetStreamHC() instead") int LZ4_resetStreamStateHC(void* state, char* inputBuffer);
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* LZ4_HC_H_19834876238432 */
+
+/*-************************************************
+ * !!!!! STATIC LINKING ONLY !!!!!
+ * Following definitions are considered experimental.
+ * They should not be linked from DLL,
+ * as there is no guarantee of API stability yet.
+ * Prototypes will be promoted to "stable" status
+ * after successfull usage in real-life scenarios.
+ *************************************************/
+#ifdef LZ4_HC_STATIC_LINKING_ONLY /* protection macro */
+#ifndef LZ4_HC_SLO_098092834
+#define LZ4_HC_SLO_098092834
+
+/*! LZ4_compress_HC_destSize() :
+ * Will try to compress as much data from `src` as possible
+ * that can fit in `targetDstSize` budget.
+ * Result is provided in 2 parts :
+ * @return : the number of bytes written into 'dst'
+ * or 0 if compression fails.
+ * `srcSizePtr` : value will be updated to indicate how much bytes were read from `src`
+ */
+LZ4LIB_API int LZ4_compress_HC_destSize(void* LZ4HC_Data,
+ const char* src, char* dst,
+ int* srcSizePtr, int targetDstSize,
+ int compressionLevel);
+
+/*! LZ4_compress_HC_continue_destSize() :
+ * Similar as LZ4_compress_HC_continue(),
+ * but will read a variable nb of bytes from `src`
+ * to fit into `targetDstSize` budget.
+ * Result is provided in 2 parts :
+ * @return : the number of bytes written into 'dst'
+ * or 0 if compression fails.
+ * `srcSizePtr` : value will be updated to indicate how much bytes were read from `src`
+ * Important : due to limitations, this prototype only works well up to cLevel < LZ4HC_CLEVEL_OPT_MIN
+ * beyond that level, compression performance will be much reduced due to internal incompatibilities
+ */
+LZ4LIB_API int LZ4_compress_HC_continue_destSize(LZ4_streamHC_t* LZ4_streamHCPtr,
+ const char* src, char* dst,
+ int* srcSizePtr, int targetDstSize);
+
+#endif /* LZ4_HC_SLO_098092834 */
+#endif /* LZ4_HC_STATIC_LINKING_ONLY */
+/*
+ lz4opt.h - Optimal Mode of LZ4
+ Copyright (C) 2015-2017, Przemyslaw Skibinski <inikep@gmail.com>
+
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ 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.
+
+ 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
+ OWNER 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.
+
+ You can contact the author at :
+ - LZ4 source repository : https://github.com/lz4/lz4
+ - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
+*/
+
+#define LZ4_OPT_NUM (1<<12)
+
+
+typedef struct {
+ int off;
+ int len;
+} LZ4HC_match_t;
+
+typedef struct {
+ int price;
+ int off;
+ int mlen;
+ int litlen;
+} LZ4HC_optimal_t;
+
+
+/* price in bits */
+FORCE_INLINE size_t LZ4HC_literalsPrice(size_t litlen)
+{
+ size_t price = litlen;
+ if (litlen >= (size_t)RUN_MASK) price += 1 + (litlen-RUN_MASK)/255;
+ return price;
+}
+
+
+/* requires mlen >= MINMATCH */
+FORCE_INLINE size_t LZ4HC_sequencePrice(size_t litlen, size_t mlen)
+{
+ size_t price = 2 + 1; /* 16-bit offset + token */
+
+ price += LZ4HC_literalsPrice(litlen);
+
+ if (mlen >= (size_t)(ML_MASK+MINMATCH))
+ price+= 1+(mlen-(ML_MASK+MINMATCH))/255;
+
+ return price;
+}
+
+
+/*=== Common LZ4 definitions ===*/
+#if defined(__GNUC__)
+# pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+#if defined (__clang__)
+# pragma clang diagnostic ignored "-Wunused-function"
+#endif
+
+//#define LZ4_COMMONDEFS_ONLY
+
+
+/*=== Constants ===*/
+#define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH)
+
+
+/*=== Macros ===*/
+#define HASH_FUNCTION(i) (((i) * 2654435761U) >> ((MINMATCH*8)-LZ4HC_HASH_LOG))
+#define DELTANEXTMAXD(p) chainTable[(p) & LZ4HC_MAXD_MASK] /* flexible, LZ4HC_MAXD dependent */
+#define DELTANEXTU16(p) chainTable[(U16)(p)] /* faster */
+static U32 LZ4HC_hashPtr(const void* ptr) { return HASH_FUNCTION(LZ4_read32(ptr)); }
+/*-*************************************
+* Binary Tree search
+***************************************/
+FORCE_INLINE int LZ4HC_BinTree_InsertAndGetAllMatches (
+ LZ4HC_CCtx_internal* ctx,
+ const BYTE* const ip,
+ const BYTE* const iHighLimit,
+ size_t best_mlen,
+ LZ4HC_match_t* matches,
+ int* matchNum)
+{
+ U16* const chainTable = ctx->chainTable;
+ U32* const HashTable = ctx->hashTable;
+ const BYTE* const base = ctx->base;
+ const U32 dictLimit = ctx->dictLimit;
+ const U32 current = (U32)(ip - base);
+ const U32 lowLimit = (ctx->lowLimit + MAX_DISTANCE > current) ? ctx->lowLimit : current - (MAX_DISTANCE - 1);
+ const BYTE* const dictBase = ctx->dictBase;
+ const BYTE* match;
+ int nbAttempts = ctx->searchNum;
+ int mnum = 0;
+ U16 *ptr0, *ptr1, delta0, delta1;
+ U32 matchIndex;
+ size_t matchLength = 0;
+ U32* HashPos;
+
+ if (ip + MINMATCH > iHighLimit) return 1;
+
+ /* HC4 match finder */
+ HashPos = &HashTable[LZ4HC_hashPtr(ip)];
+ matchIndex = *HashPos;
+ *HashPos = current;
+
+ ptr0 = &DELTANEXTMAXD(current*2+1);
+ ptr1 = &DELTANEXTMAXD(current*2);
+ delta0 = delta1 = (U16)(current - matchIndex);
+
+ while ((matchIndex < current) && (matchIndex>=lowLimit) && (nbAttempts)) {
+ nbAttempts--;
+ if (matchIndex >= dictLimit) {
+ match = base + matchIndex;
+ matchLength = LZ4_count(ip, match, iHighLimit);
+ } else {
+ const BYTE* vLimit = ip + (dictLimit - matchIndex);
+ match = dictBase + matchIndex;
+ if (vLimit > iHighLimit) vLimit = iHighLimit;
+ matchLength = LZ4_count(ip, match, vLimit);
+ if ((ip+matchLength == vLimit) && (vLimit < iHighLimit))
+ matchLength += LZ4_count(ip+matchLength, base+dictLimit, iHighLimit);
+ }
+
+ if (matchLength > best_mlen) {
+ best_mlen = matchLength;
+ if (matches) {
+ if (matchIndex >= dictLimit)
+ matches[mnum].off = (int)(ip - match);
+ else
+ matches[mnum].off = (int)(ip - (base + matchIndex)); /* virtual matchpos */
+ matches[mnum].len = (int)matchLength;
+ mnum++;
+ }
+ if (best_mlen > LZ4_OPT_NUM) break;
+ }
+
+ if (ip+matchLength >= iHighLimit) /* equal : no way to know if inf or sup */
+ break; /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt the tree */
+
+ if (*(ip+matchLength) < *(match+matchLength)) {
+ *ptr0 = delta0;
+ ptr0 = &DELTANEXTMAXD(matchIndex*2);
+ if (*ptr0 == (U16)-1) break;
+ delta0 = *ptr0;
+ delta1 += delta0;
+ matchIndex -= delta0;
+ } else {
+ *ptr1 = delta1;
+ ptr1 = &DELTANEXTMAXD(matchIndex*2+1);
+ if (*ptr1 == (U16)-1) break;
+ delta1 = *ptr1;
+ delta0 += delta1;
+ matchIndex -= delta1;
+ }
+ }
+
+ *ptr0 = (U16)-1;
+ *ptr1 = (U16)-1;
+ if (matchNum) *matchNum = mnum;
+ /* if (best_mlen > 8) return best_mlen-8; */
+ if (!matchNum) return 1;
+ return 1;
+}
+
+
+FORCE_INLINE void LZ4HC_updateBinTree(LZ4HC_CCtx_internal* ctx, const BYTE* const ip, const BYTE* const iHighLimit)
+{
+ const BYTE* const base = ctx->base;
+ const U32 target = (U32)(ip - base);
+ U32 idx = ctx->nextToUpdate;
+ while(idx < target)
+ idx += LZ4HC_BinTree_InsertAndGetAllMatches(ctx, base+idx, iHighLimit, 8, NULL, NULL);
+}
+
+
+/** Tree updater, providing best match */
+FORCE_INLINE int LZ4HC_BinTree_GetAllMatches (
+ LZ4HC_CCtx_internal* ctx,
+ const BYTE* const ip, const BYTE* const iHighLimit,
+ size_t best_mlen, LZ4HC_match_t* matches, const int fullUpdate)
+{
+ int mnum = 0;
+ if (ip < ctx->base + ctx->nextToUpdate) return 0; /* skipped area */
+ if (fullUpdate) LZ4HC_updateBinTree(ctx, ip, iHighLimit);
+ best_mlen = LZ4HC_BinTree_InsertAndGetAllMatches(ctx, ip, iHighLimit, best_mlen, matches, &mnum);
+ ctx->nextToUpdate = (U32)(ip - ctx->base + best_mlen);
+ return mnum;
+}
+
+
+#define SET_PRICE(pos, ml, offset, ll, cost) \
+{ \
+ while (last_pos < pos) { opt[last_pos+1].price = 1<<30; last_pos++; } \
+ opt[pos].mlen = (int)ml; \
+ opt[pos].off = (int)offset; \
+ opt[pos].litlen = (int)ll; \
+ opt[pos].price = (int)cost; \
+}
+
+FORCE_INLINE int LZ4HC_encodeSequence ( const BYTE** ip, BYTE** op, const BYTE** anchor, int matchLength, const BYTE* const match, limitedOutput_directive limit, BYTE* oend);
+
+static int LZ4HC_compress_optimal (
+ LZ4HC_CCtx_internal* ctx,
+ const char* const source,
+ char* dest,
+ int inputSize,
+ int maxOutputSize,
+ limitedOutput_directive limit,
+ size_t sufficient_len,
+ const int fullUpdate
+ )
+{
+ LZ4HC_optimal_t opt[LZ4_OPT_NUM + 1]; /* this uses a bit too much stack memory to my taste ... */
+ LZ4HC_match_t matches[LZ4_OPT_NUM + 1];
+
+ const BYTE* ip = (const BYTE*) source;
+ const BYTE* anchor = ip;
+ const BYTE* const iend = ip + inputSize;
+ const BYTE* const mflimit = iend - MFLIMIT;
+ const BYTE* const matchlimit = (iend - LASTLITERALS);
+ BYTE* op = (BYTE*) dest;
+ BYTE* const oend = op + maxOutputSize;
+
+ /* init */
+ if (sufficient_len >= LZ4_OPT_NUM) sufficient_len = LZ4_OPT_NUM-1;
+ ctx->end += inputSize;
+ ip++;
+
+ /* Main Loop */
+ while (ip < mflimit) {
+ size_t const llen = ip - anchor;
+ size_t last_pos = 0;
+ size_t match_num, cur, best_mlen, best_off;
+ memset(opt, 0, sizeof(LZ4HC_optimal_t));
+
+ match_num = LZ4HC_BinTree_GetAllMatches(ctx, ip, matchlimit, MINMATCH-1, matches, fullUpdate);
+ if (!match_num) { ip++; continue; }
+
+ if ((size_t)matches[match_num-1].len > sufficient_len) {
+ /* good enough solution : immediate encoding */
+ best_mlen = matches[match_num-1].len;
+ best_off = matches[match_num-1].off;
+ cur = 0;
+ last_pos = 1;
+ goto encode;
+ }
+
+ /* set prices using matches at position = 0 */
+ { size_t matchNb;
+ for (matchNb = 0; matchNb < match_num; matchNb++) {
+ size_t mlen = (matchNb>0) ? (size_t)matches[matchNb-1].len+1 : MINMATCH;
+ best_mlen = matches[matchNb].len; /* necessarily < sufficient_len < LZ4_OPT_NUM */
+ for ( ; mlen <= best_mlen ; mlen++) {
+ size_t const cost = LZ4HC_sequencePrice(llen, mlen) - LZ4HC_literalsPrice(llen);
+ SET_PRICE(mlen, mlen, matches[matchNb].off, 0, cost); /* updates last_pos and opt[pos] */
+ } } }
+
+ if (last_pos < MINMATCH) { ip++; continue; } /* note : on clang at least, this test improves performance */
+
+ /* check further positions */
+ opt[0].mlen = opt[1].mlen = 1;
+ for (cur = 1; cur <= last_pos; cur++) {
+ const BYTE* const curPtr = ip + cur;
+
+ /* establish baseline price if cur is literal */
+ { size_t price, litlen;
+ if (opt[cur-1].mlen == 1) {
+ /* no match at previous position */
+ litlen = opt[cur-1].litlen + 1;
+ if (cur > litlen) {
+ price = opt[cur - litlen].price + LZ4HC_literalsPrice(litlen);
+ } else {
+ price = LZ4HC_literalsPrice(llen + litlen) - LZ4HC_literalsPrice(llen);
+ }
+ } else {
+ litlen = 1;
+ price = opt[cur - 1].price + LZ4HC_literalsPrice(1);
+ }
+
+ if (price < (size_t)opt[cur].price)
+ SET_PRICE(cur, 1, 0, litlen, price); /* note : increases last_pos */
+ }
+
+ if (cur == last_pos || curPtr >= mflimit) break;
+
+ match_num = LZ4HC_BinTree_GetAllMatches(ctx, curPtr, matchlimit, MINMATCH-1, matches, fullUpdate);
+ if ((match_num > 0) && (size_t)matches[match_num-1].len > sufficient_len) {
+ /* immediate encoding */
+ best_mlen = matches[match_num-1].len;
+ best_off = matches[match_num-1].off;
+ last_pos = cur + 1;
+ goto encode;
+ }
+
+ /* set prices using matches at position = cur */
+ { size_t matchNb;
+ for (matchNb = 0; matchNb < match_num; matchNb++) {
+ size_t ml = (matchNb>0) ? (size_t)matches[matchNb-1].len+1 : MINMATCH;
+ best_mlen = (cur + matches[matchNb].len < LZ4_OPT_NUM) ?
+ (size_t)matches[matchNb].len : LZ4_OPT_NUM - cur;
+
+ for ( ; ml <= best_mlen ; ml++) {
+ size_t ll, price;
+ if (opt[cur].mlen == 1) {
+ ll = opt[cur].litlen;
+ if (cur > ll)
+ price = opt[cur - ll].price + LZ4HC_sequencePrice(ll, ml);
+ else
+ price = LZ4HC_sequencePrice(llen + ll, ml) - LZ4HC_literalsPrice(llen);
+ } else {
+ ll = 0;
+ price = opt[cur].price + LZ4HC_sequencePrice(0, ml);
+ }
+
+ if (cur + ml > last_pos || price < (size_t)opt[cur + ml].price) {
+ SET_PRICE(cur + ml, ml, matches[matchNb].off, ll, price);
+ } } } }
+ } /* for (cur = 1; cur <= last_pos; cur++) */
+
+ best_mlen = opt[last_pos].mlen;
+ best_off = opt[last_pos].off;
+ cur = last_pos - best_mlen;
+
+encode: /* cur, last_pos, best_mlen, best_off must be set */
+ opt[0].mlen = 1;
+ while (1) { /* from end to beginning */
+ size_t const ml = opt[cur].mlen;
+ int const offset = opt[cur].off;
+ opt[cur].mlen = (int)best_mlen;
+ opt[cur].off = (int)best_off;
+ best_mlen = ml;
+ best_off = offset;
+ if (ml > cur) break;
+ cur -= ml;
+ }
+
+ /* encode all recorded sequences */
+ cur = 0;
+ while (cur < last_pos) {
+ int const ml = opt[cur].mlen;
+ int const offset = opt[cur].off;
+ if (ml == 1) { ip++; cur++; continue; }
+ cur += ml;
+ if ( LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ip - offset, limit, oend) ) return 0;
+ }
+ } /* while (ip < mflimit) */
+
+ /* Encode Last Literals */
+ { int lastRun = (int)(iend - anchor);
+ if ((limit) && (((char*)op - dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize)) return 0; /* Check output limit */
+ if (lastRun>=(int)RUN_MASK) { *op++=(RUN_MASK<<ML_BITS); lastRun-=RUN_MASK; for(; lastRun > 254 ; lastRun-=255) *op++ = 255; *op++ = (BYTE) lastRun; }
+ else *op++ = (BYTE)(lastRun<<ML_BITS);
+ memcpy(op, anchor, iend - anchor);
+ op += iend-anchor;
+ }
+
+ /* End */
+ return (int) ((char*)op-dest);
+}
+/*
+ LZ4 HC - High Compression Mode of LZ4
+ Copyright (C) 2011-2017, Yann Collet.
+
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ 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.
+
+ 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
+ OWNER 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.
+
+ You can contact the author at :
+ - LZ4 source repository : https://github.com/lz4/lz4
+ - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
+*/
+/* note : lz4hc is not an independent module, it requires lz4.h/lz4.c for proper compilation */
+
+
+/* *************************************
+* Tuning Parameter
+***************************************/
+
+/*! HEAPMODE :
+ * Select how default compression function will allocate workplace memory,
+ * in stack (0:fastest), or in heap (1:requires malloc()).
+ * Since workplace is rather large, heap mode is recommended.
+ */
+#ifndef LZ4HC_HEAPMODE
+# define LZ4HC_HEAPMODE 1
+#endif
+
+
+/*=== Dependency ===*/
+
+
+
+
+
+
+/**************************************
+* HC Compression
+**************************************/
+static void LZ4HC_init (LZ4HC_CCtx_internal* hc4, const BYTE* start)
+{
+ MEM_INIT((void*)hc4->hashTable, 0, sizeof(hc4->hashTable));
+ MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable));
+ hc4->nextToUpdate = 64 KB;
+ hc4->base = start - 64 KB;
+ hc4->end = start;
+ hc4->dictBase = start - 64 KB;
+ hc4->dictLimit = 64 KB;
+ hc4->lowLimit = 64 KB;
+}
+
+
+/* Update chains up to ip (excluded) */
+FORCE_INLINE void LZ4HC_Insert (LZ4HC_CCtx_internal* hc4, const BYTE* ip)
+{
+ U16* const chainTable = hc4->chainTable;
+ U32* const hashTable = hc4->hashTable;
+ const BYTE* const base = hc4->base;
+ U32 const target = (U32)(ip - base);
+ U32 idx = hc4->nextToUpdate;
+
+ while (idx < target) {
+ U32 const h = LZ4HC_hashPtr(base+idx);
+ size_t delta = idx - hashTable[h];
+ if (delta>MAX_DISTANCE) delta = MAX_DISTANCE;
+ DELTANEXTU16(idx) = (U16)delta;
+ hashTable[h] = idx;
+ idx++;
+ }
+
+ hc4->nextToUpdate = target;
+}
+
+
+FORCE_INLINE int LZ4HC_InsertAndFindBestMatch (LZ4HC_CCtx_internal* hc4, /* Index table will be updated */
+ const BYTE* ip, const BYTE* const iLimit,
+ const BYTE** matchpos,
+ const int maxNbAttempts)
+{
+ U16* const chainTable = hc4->chainTable;
+ U32* const HashTable = hc4->hashTable;
+ const BYTE* const base = hc4->base;
+ const BYTE* const dictBase = hc4->dictBase;
+ const U32 dictLimit = hc4->dictLimit;
+ const U32 lowLimit = (hc4->lowLimit + 64 KB > (U32)(ip-base)) ? hc4->lowLimit : (U32)(ip - base) - (64 KB - 1);
+ U32 matchIndex;
+ int nbAttempts = maxNbAttempts;
+ size_t ml = 0;
+
+ /* HC4 match finder */
+ LZ4HC_Insert(hc4, ip);
+ matchIndex = HashTable[LZ4HC_hashPtr(ip)];
+
+ while ((matchIndex>=lowLimit) && (nbAttempts)) {
+ nbAttempts--;
+ if (matchIndex >= dictLimit) {
+ const BYTE* const match = base + matchIndex;
+ if (*(match+ml) == *(ip+ml)
+ && (LZ4_read32(match) == LZ4_read32(ip)))
+ {
+ size_t const mlt = LZ4_count(ip+MINMATCH, match+MINMATCH, iLimit) + MINMATCH;
+ if (mlt > ml) { ml = mlt; *matchpos = match; }
+ }
+ } else {
+ const BYTE* const match = dictBase + matchIndex;
+ if (LZ4_read32(match) == LZ4_read32(ip)) {
+ size_t mlt;
+ const BYTE* vLimit = ip + (dictLimit - matchIndex);
+ if (vLimit > iLimit) vLimit = iLimit;
+ mlt = LZ4_count(ip+MINMATCH, match+MINMATCH, vLimit) + MINMATCH;
+ if ((ip+mlt == vLimit) && (vLimit < iLimit))
+ mlt += LZ4_count(ip+mlt, base+dictLimit, iLimit);
+ if (mlt > ml) { ml = mlt; *matchpos = base + matchIndex; } /* virtual matchpos */
+ }
+ }
+ matchIndex -= DELTANEXTU16(matchIndex);
+ }
+
+ return (int)ml;
+}
+
+
+FORCE_INLINE int LZ4HC_InsertAndGetWiderMatch (
+ LZ4HC_CCtx_internal* hc4,
+ const BYTE* const ip,
+ const BYTE* const iLowLimit,
+ const BYTE* const iHighLimit,
+ int longest,
+ const BYTE** matchpos,
+ const BYTE** startpos,
+ const int maxNbAttempts)
+{
+ U16* const chainTable = hc4->chainTable;
+ U32* const HashTable = hc4->hashTable;
+ const BYTE* const base = hc4->base;
+ const U32 dictLimit = hc4->dictLimit;
+ const BYTE* const lowPrefixPtr = base + dictLimit;
+ const U32 lowLimit = (hc4->lowLimit + 64 KB > (U32)(ip-base)) ? hc4->lowLimit : (U32)(ip - base) - (64 KB - 1);
+ const BYTE* const dictBase = hc4->dictBase;
+ U32 matchIndex;
+ int nbAttempts = maxNbAttempts;
+ int delta = (int)(ip-iLowLimit);
+
+
+ /* First Match */
+ LZ4HC_Insert(hc4, ip);
+ matchIndex = HashTable[LZ4HC_hashPtr(ip)];
+
+ while ((matchIndex>=lowLimit) && (nbAttempts)) {
+ nbAttempts--;
+ if (matchIndex >= dictLimit) {
+ const BYTE* matchPtr = base + matchIndex;
+ if (*(iLowLimit + longest) == *(matchPtr - delta + longest)) {
+ if (LZ4_read32(matchPtr) == LZ4_read32(ip)) {
+ int mlt = MINMATCH + LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, iHighLimit);
+ int back = 0;
+
+ while ((ip+back > iLowLimit)
+ && (matchPtr+back > lowPrefixPtr)
+ && (ip[back-1] == matchPtr[back-1]))
+ back--;
+
+ mlt -= back;
+
+ if (mlt > longest) {
+ longest = (int)mlt;
+ *matchpos = matchPtr+back;
+ *startpos = ip+back;
+ } } }
+ } else {
+ const BYTE* const matchPtr = dictBase + matchIndex;
+ if (LZ4_read32(matchPtr) == LZ4_read32(ip)) {
+ size_t mlt;
+ int back=0;
+ const BYTE* vLimit = ip + (dictLimit - matchIndex);
+ if (vLimit > iHighLimit) vLimit = iHighLimit;
+ mlt = LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH;
+ if ((ip+mlt == vLimit) && (vLimit < iHighLimit))
+ mlt += LZ4_count(ip+mlt, base+dictLimit, iHighLimit);
+ while ((ip+back > iLowLimit) && (matchIndex+back > lowLimit) && (ip[back-1] == matchPtr[back-1])) back--;
+ mlt -= back;
+ if ((int)mlt > longest) { longest = (int)mlt; *matchpos = base + matchIndex + back; *startpos = ip+back; }
+ }
+ }
+ matchIndex -= DELTANEXTU16(matchIndex);
+ }
+
+ return longest;
+}
+
+
+
+#define LZ4HC_DEBUG 0
+#if LZ4HC_DEBUG
+static unsigned debug = 0;
+#endif
+
+
+/* LZ4HC_encodeSequence() :
+ * @return : 0 if ok,
+ * 1 if buffer issue detected */
+FORCE_INLINE int LZ4HC_encodeSequence ( const BYTE** ip, BYTE** op, const BYTE** anchor, int matchLength, const BYTE* const match, limitedOutput_directive limit, BYTE* oend)
+{
+ size_t length;
+ BYTE* token;
+
+#if LZ4HC_DEBUG
+ if (debug) printf("literal : %u -- match : %u -- offset : %u\n", (U32)(*ip - *anchor), (U32)matchLength, (U32)(*ip-match));
+#endif
+
+ /* Encode Literal length */
+ length = (size_t)(*ip - *anchor);
+ token = (*op)++;
+ if ((limit) && ((*op + (length >> 8) + length + (2 + 1 + LASTLITERALS)) > oend)) return 1; /* Check output limit */
+ if (length >= RUN_MASK) {
+ size_t len = length - RUN_MASK;
+ *token = (RUN_MASK << ML_BITS);
+ for(; len >= 255 ; len -= 255) *(*op)++ = 255;
+ *(*op)++ = (BYTE)len;
+ } else {
+ *token = (BYTE)(length << ML_BITS);
+ }
+
+ /* Copy Literals */
+ LZ4_wildCopy(*op, *anchor, (*op) + length);
+ *op += length;
+
+ /* Encode Offset */
+ LZ4_writeLE16(*op, (U16)(*ip-match)); *op += 2;
+
+ /* Encode MatchLength */
+ length = (size_t)(matchLength - MINMATCH);
+ if ((limit) && (*op + (length >> 8) + (1 + LASTLITERALS) > oend)) return 1; /* Check output limit */
+ if (length >= ML_MASK) {
+ *token += ML_MASK;
+ length -= ML_MASK;
+ for(; length >= 510 ; length -= 510) { *(*op)++ = 255; *(*op)++ = 255; }
+ if (length >= 255) { length -= 255; *(*op)++ = 255; }
+ *(*op)++ = (BYTE)length;
+ } else {
+ *token += (BYTE)(length);
+ }
+
+ /* Prepare next loop */
+ *ip += matchLength;
+ *anchor = *ip;
+
+ return 0;
+}
+
+/* btopt */
+
+
+static int LZ4HC_compress_hashChain (
+ LZ4HC_CCtx_internal* const ctx,
+ const char* const source,
+ char* const dest,
+ int* srcSizePtr,
+ int const maxOutputSize,
+ unsigned maxNbAttempts,
+ limitedOutput_directive limit
+ )
+{
+ const int inputSize = *srcSizePtr;
+
+ const BYTE* ip = (const BYTE*) source;
+ const BYTE* anchor = ip;
+ const BYTE* const iend = ip + inputSize;
+ const BYTE* const mflimit = iend - MFLIMIT;
+ const BYTE* const matchlimit = (iend - LASTLITERALS);
+
+ BYTE* optr = (BYTE*) dest;
+ BYTE* op = (BYTE*) dest;
+ BYTE* oend = op + maxOutputSize;
+
+ int ml, ml2, ml3, ml0;
+ const BYTE* ref = NULL;
+ const BYTE* start2 = NULL;
+ const BYTE* ref2 = NULL;
+ const BYTE* start3 = NULL;
+ const BYTE* ref3 = NULL;
+ const BYTE* start0;
+ const BYTE* ref0;
+
+ /* init */
+ *srcSizePtr = 0;
+ if (limit == limitedDestSize && maxOutputSize < 1) return 0; /* Impossible to store anything */
+ if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */
+
+ ctx->end += inputSize;
+ if (limit == limitedDestSize) oend -= LASTLITERALS; /* Hack for support limitations LZ4 decompressor */
+ if (inputSize < LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
+
+ ip++;
+
+ /* Main Loop */
+ while (ip < mflimit) {
+ ml = LZ4HC_InsertAndFindBestMatch (ctx, ip, matchlimit, (&ref), maxNbAttempts);
+ if (!ml) { ip++; continue; }
+
+ /* saved, in case we would skip too much */
+ start0 = ip;
+ ref0 = ref;
+ ml0 = ml;
+
+_Search2:
+ if (ip+ml < mflimit)
+ ml2 = LZ4HC_InsertAndGetWiderMatch(ctx, ip + ml - 2, ip + 0, matchlimit, ml, &ref2, &start2, maxNbAttempts);
+ else
+ ml2 = ml;
+
+ if (ml2 == ml) { /* No better match */
+ optr = op;
+ if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) goto _dest_overflow;
+ continue;
+ }
+
+ if (start0 < ip) {
+ if (start2 < ip + ml0) { /* empirical */
+ ip = start0;
+ ref = ref0;
+ ml = ml0;
+ }
+ }
+
+ /* Here, start0==ip */
+ if ((start2 - ip) < 3) { /* First Match too small : removed */
+ ml = ml2;
+ ip = start2;
+ ref =ref2;
+ goto _Search2;
+ }
+
+_Search3:
+ /* At this stage, we have :
+ * ml2 > ml1, and
+ * ip1+3 <= ip2 (usually < ip1+ml1) */
+ if ((start2 - ip) < OPTIMAL_ML) {
+ int correction;
+ int new_ml = ml;
+ if (new_ml > OPTIMAL_ML) new_ml = OPTIMAL_ML;
+ if (ip+new_ml > start2 + ml2 - MINMATCH) new_ml = (int)(start2 - ip) + ml2 - MINMATCH;
+ correction = new_ml - (int)(start2 - ip);
+ if (correction > 0) {
+ start2 += correction;
+ ref2 += correction;
+ ml2 -= correction;
+ }
+ }
+ /* Now, we have start2 = ip+new_ml, with new_ml = min(ml, OPTIMAL_ML=18) */
+
+ if (start2 + ml2 < mflimit)
+ ml3 = LZ4HC_InsertAndGetWiderMatch(ctx, start2 + ml2 - 3, start2, matchlimit, ml2, &ref3, &start3, maxNbAttempts);
+ else
+ ml3 = ml2;
+
+ if (ml3 == ml2) { /* No better match : 2 sequences to encode */
+ /* ip & ref are known; Now for ml */
+ if (start2 < ip+ml) ml = (int)(start2 - ip);
+ /* Now, encode 2 sequences */
+ optr = op;
+ if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) goto _dest_overflow;
+ ip = start2;
+ optr = op;
+ if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml2, ref2, limit, oend)) goto _dest_overflow;
+ continue;
+ }
+
+ if (start3 < ip+ml+3) { /* Not enough space for match 2 : remove it */
+ if (start3 >= (ip+ml)) { /* can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1 */
+ if (start2 < ip+ml) {
+ int correction = (int)(ip+ml - start2);
+ start2 += correction;
+ ref2 += correction;
+ ml2 -= correction;
+ if (ml2 < MINMATCH) {
+ start2 = start3;
+ ref2 = ref3;
+ ml2 = ml3;
+ }
+ }
+
+ optr = op;
+ if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) goto _dest_overflow;
+ ip = start3;
+ ref = ref3;
+ ml = ml3;
+
+ start0 = start2;
+ ref0 = ref2;
+ ml0 = ml2;
+ goto _Search2;
+ }
+
+ start2 = start3;
+ ref2 = ref3;
+ ml2 = ml3;
+ goto _Search3;
+ }
+
+ /*
+ * OK, now we have 3 ascending matches; let's write at least the first one
+ * ip & ref are known; Now for ml
+ */
+ if (start2 < ip+ml) {
+ if ((start2 - ip) < (int)ML_MASK) {
+ int correction;
+ if (ml > OPTIMAL_ML) ml = OPTIMAL_ML;
+ if (ip + ml > start2 + ml2 - MINMATCH) ml = (int)(start2 - ip) + ml2 - MINMATCH;
+ correction = ml - (int)(start2 - ip);
+ if (correction > 0) {
+ start2 += correction;
+ ref2 += correction;
+ ml2 -= correction;
+ }
+ } else {
+ ml = (int)(start2 - ip);
+ }
+ }
+ optr = op;
+ if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) goto _dest_overflow;
+
+ ip = start2;
+ ref = ref2;
+ ml = ml2;
+
+ start2 = start3;
+ ref2 = ref3;
+ ml2 = ml3;
+
+ goto _Search3;
+ }
+
+_last_literals:
+ /* Encode Last Literals */
+ { size_t lastRunSize = (size_t)(iend - anchor); /* literals */
+ size_t litLength = (lastRunSize + 255 - RUN_MASK) / 255;
+ size_t const totalSize = 1 + litLength + lastRunSize;
+ if (limit == limitedDestSize) oend += LASTLITERALS; /* restore correct value */
+ if (limit && (op + totalSize > oend)) {
+ if (limit == limitedOutput) return 0; /* Check output limit */
+ /* adapt lastRunSize to fill 'dest' */
+ lastRunSize = (size_t)(oend - op) - 1;
+ litLength = (lastRunSize + 255 - RUN_MASK) / 255;
+ lastRunSize -= litLength;
+ }
+ ip = anchor + lastRunSize;
+
+ if (lastRunSize >= RUN_MASK) {
+ size_t accumulator = lastRunSize - RUN_MASK;
+ *op++ = (RUN_MASK << ML_BITS);
+ for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255;
+ *op++ = (BYTE) accumulator;
+ } else {
+ *op++ = (BYTE)(lastRunSize << ML_BITS);
+ }
+ memcpy(op, anchor, lastRunSize);
+ op += lastRunSize;
+ }
+
+ /* End */
+ *srcSizePtr = (int) (((const char*)ip) - source);
+ return (int) (((char*)op)-dest);
+
+_dest_overflow:
+ if (limit == limitedDestSize) {
+ op = optr; /* restore correct out pointer */
+ goto _last_literals;
+ }
+ return 0;
+}
+
+static int LZ4HC_getSearchNum(int compressionLevel)
+{
+ switch (compressionLevel) {
+ default: return 0; /* unused */
+ case 11: return 128;
+ case 12: return 1<<10;
+ }
+}
+
+static int LZ4HC_compress_generic (
+ LZ4HC_CCtx_internal* const ctx,
+ const char* const src,
+ char* const dst,
+ int* const srcSizePtr,
+ int const dstCapacity,
+ int cLevel,
+ limitedOutput_directive limit
+ )
+{
+ if (cLevel < 1) cLevel = LZ4HC_CLEVEL_DEFAULT; /* note : convention is different from lz4frame, maybe to reconsider */
+ if (cLevel > 9) {
+ if (limit == limitedDestSize) cLevel = 10;
+ switch (cLevel) {
+ case 10:
+ return LZ4HC_compress_hashChain(ctx, src, dst, srcSizePtr, dstCapacity, 1 << (15-1), limit);
+ case 11:
+ ctx->searchNum = LZ4HC_getSearchNum(cLevel);
+ return LZ4HC_compress_optimal(ctx, src, dst, *srcSizePtr, dstCapacity, limit, 128, 0);
+ default:
+ case 12:
+ ctx->searchNum = LZ4HC_getSearchNum(cLevel);
+ return LZ4HC_compress_optimal(ctx, src, dst, *srcSizePtr, dstCapacity, limit, LZ4_OPT_NUM, 1);
+ }
+ }
+ return LZ4HC_compress_hashChain(ctx, src, dst, srcSizePtr, dstCapacity, 1 << (cLevel-1), limit); /* levels 1-9 */
+}
+
+
+int LZ4_sizeofStateHC(void) { return sizeof(LZ4_streamHC_t); }
+
+int LZ4_compress_HC_extStateHC (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel)
+{
+ LZ4HC_CCtx_internal* const ctx = &((LZ4_streamHC_t*)state)->internal_donotuse;
+ if (((size_t)(state)&(sizeof(void*)-1)) != 0) return 0; /* Error : state is not aligned for pointers (32 or 64 bits) */
+ LZ4HC_init (ctx, (const BYTE*)src);
+ if (dstCapacity < LZ4_compressBound(srcSize))
+ return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, limitedOutput);
+ else
+ return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, noLimit);
+}
+
+int LZ4_compress_HC(const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel)
+{
+#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1
+ LZ4_streamHC_t* const statePtr = (LZ4_streamHC_t*)malloc(sizeof(LZ4_streamHC_t));
+#else
+ LZ4_streamHC_t state;
+ LZ4_streamHC_t* const statePtr = &state;
+#endif
+ int const cSize = LZ4_compress_HC_extStateHC(statePtr, src, dst, srcSize, dstCapacity, compressionLevel);
+#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1
+ free(statePtr);
+#endif
+ return cSize;
+}
+
+/* LZ4_compress_HC_destSize() :
+ * currently, only compatible with Hash Chain implementation,
+ * hence limit compression level to LZ4HC_CLEVEL_OPT_MIN-1*/
+int LZ4_compress_HC_destSize(void* LZ4HC_Data, const char* source, char* dest, int* sourceSizePtr, int targetDestSize, int cLevel)
+{
+ LZ4HC_CCtx_internal* const ctx = &((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse;
+ LZ4HC_init(ctx, (const BYTE*) source);
+ return LZ4HC_compress_generic(ctx, source, dest, sourceSizePtr, targetDestSize, cLevel, limitedDestSize);
+}
+
+
+
+/**************************************
+* Streaming Functions
+**************************************/
+/* allocation */
+LZ4_streamHC_t* LZ4_createStreamHC(void) { return (LZ4_streamHC_t*)malloc(sizeof(LZ4_streamHC_t)); }
+int LZ4_freeStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr) { free(LZ4_streamHCPtr); return 0; }
+
+
+/* initialization */
+void LZ4_resetStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel)
+{
+ LZ4_STATIC_ASSERT(sizeof(LZ4HC_CCtx_internal) <= sizeof(size_t) * LZ4_STREAMHCSIZE_SIZET); /* if compilation fails here, LZ4_STREAMHCSIZE must be increased */
+ LZ4_streamHCPtr->internal_donotuse.base = NULL;
+ if (compressionLevel > LZ4HC_CLEVEL_MAX) compressionLevel = LZ4HC_CLEVEL_MAX; /* cap compression level */
+ LZ4_streamHCPtr->internal_donotuse.compressionLevel = compressionLevel;
+ LZ4_streamHCPtr->internal_donotuse.searchNum = LZ4HC_getSearchNum(compressionLevel);
+}
+
+int LZ4_loadDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, const char* dictionary, int dictSize)
+{
+ LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse;
+ if (dictSize > 64 KB) {
+ dictionary += dictSize - 64 KB;
+ dictSize = 64 KB;
+ }
+ LZ4HC_init (ctxPtr, (const BYTE*)dictionary);
+ ctxPtr->end = (const BYTE*)dictionary + dictSize;
+ if (ctxPtr->compressionLevel >= LZ4HC_CLEVEL_OPT_MIN)
+ LZ4HC_updateBinTree(ctxPtr, ctxPtr->end - MFLIMIT, ctxPtr->end - LASTLITERALS);
+ else
+ if (dictSize >= 4) LZ4HC_Insert (ctxPtr, ctxPtr->end-3);
+ return dictSize;
+}
+
+
+/* compression */
+
+static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock)
+{
+ if (ctxPtr->compressionLevel >= LZ4HC_CLEVEL_OPT_MIN)
+ LZ4HC_updateBinTree(ctxPtr, ctxPtr->end - MFLIMIT, ctxPtr->end - LASTLITERALS);
+ else
+ if (ctxPtr->end >= ctxPtr->base + 4) LZ4HC_Insert (ctxPtr, ctxPtr->end-3); /* Referencing remaining dictionary content */
+
+ /* Only one memory segment for extDict, so any previous extDict is lost at this stage */
+ ctxPtr->lowLimit = ctxPtr->dictLimit;
+ ctxPtr->dictLimit = (U32)(ctxPtr->end - ctxPtr->base);
+ ctxPtr->dictBase = ctxPtr->base;
+ ctxPtr->base = newBlock - ctxPtr->dictLimit;
+ ctxPtr->end = newBlock;
+ ctxPtr->nextToUpdate = ctxPtr->dictLimit; /* match referencing will resume from there */
+}
+
+static int LZ4_compressHC_continue_generic (LZ4_streamHC_t* LZ4_streamHCPtr,
+ const char* src, char* dst,
+ int* srcSizePtr, int dstCapacity,
+ limitedOutput_directive limit)
+{
+ LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse;
+ /* auto-init if forgotten */
+ if (ctxPtr->base == NULL) LZ4HC_init (ctxPtr, (const BYTE*) src);
+
+ /* Check overflow */
+ if ((size_t)(ctxPtr->end - ctxPtr->base) > 2 GB) {
+ size_t dictSize = (size_t)(ctxPtr->end - ctxPtr->base) - ctxPtr->dictLimit;
+ if (dictSize > 64 KB) dictSize = 64 KB;
+ LZ4_loadDictHC(LZ4_streamHCPtr, (const char*)(ctxPtr->end) - dictSize, (int)dictSize);
+ }
+
+ /* Check if blocks follow each other */
+ if ((const BYTE*)src != ctxPtr->end) LZ4HC_setExternalDict(ctxPtr, (const BYTE*)src);
+
+ /* Check overlapping input/dictionary space */
+ { const BYTE* sourceEnd = (const BYTE*) src + *srcSizePtr;
+ const BYTE* const dictBegin = ctxPtr->dictBase + ctxPtr->lowLimit;
+ const BYTE* const dictEnd = ctxPtr->dictBase + ctxPtr->dictLimit;
+ if ((sourceEnd > dictBegin) && ((const BYTE*)src < dictEnd)) {
+ if (sourceEnd > dictEnd) sourceEnd = dictEnd;
+ ctxPtr->lowLimit = (U32)(sourceEnd - ctxPtr->dictBase);
+ if (ctxPtr->dictLimit - ctxPtr->lowLimit < 4) ctxPtr->lowLimit = ctxPtr->dictLimit;
+ }
+ }
+
+ return LZ4HC_compress_generic (ctxPtr, src, dst, srcSizePtr, dstCapacity, ctxPtr->compressionLevel, limit);
+}
+
+int LZ4_compress_HC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int srcSize, int dstCapacity)
+{
+ if (dstCapacity < LZ4_compressBound(srcSize))
+ return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, limitedOutput);
+ else
+ return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, noLimit);
+}
+
+int LZ4_compress_HC_continue_destSize (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int* srcSizePtr, int targetDestSize)
+{
+ LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse;
+ if (ctxPtr->compressionLevel >= LZ4HC_CLEVEL_OPT_MIN) LZ4HC_init(ctxPtr, (const BYTE*)src); /* not compatible with btopt implementation */
+ return LZ4_compressHC_continue_generic(LZ4_streamHCPtr, src, dst, srcSizePtr, targetDestSize, limitedDestSize);
+}
+
+
+
+/* dictionary saving */
+
+int LZ4_saveDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, char* safeBuffer, int dictSize)
+{
+ LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse;
+ int const prefixSize = (int)(streamPtr->end - (streamPtr->base + streamPtr->dictLimit));
+ if (dictSize > 64 KB) dictSize = 64 KB;
+ if (dictSize < 4) dictSize = 0;
+ if (dictSize > prefixSize) dictSize = prefixSize;
+ memmove(safeBuffer, streamPtr->end - dictSize, dictSize);
+ { U32 const endIndex = (U32)(streamPtr->end - streamPtr->base);
+ streamPtr->end = (const BYTE*)safeBuffer + dictSize;
+ streamPtr->base = streamPtr->end - endIndex;
+ streamPtr->dictLimit = endIndex - dictSize;
+ streamPtr->lowLimit = endIndex - dictSize;
+ if (streamPtr->nextToUpdate < streamPtr->dictLimit) streamPtr->nextToUpdate = streamPtr->dictLimit;
+ }
+ return dictSize;
+}
+
+
+/***********************************
+* Deprecated Functions
+***********************************/
+/* These functions currently generate deprecation warnings */
+/* Deprecated compression functions */
+int LZ4_compressHC(const char* src, char* dst, int srcSize) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), 0); }
+int LZ4_compressHC_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, 0); }
+int LZ4_compressHC2(const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); }
+int LZ4_compressHC2_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, cLevel); }
+int LZ4_compressHC_withStateHC (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, LZ4_compressBound(srcSize), 0); }
+int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, maxDstSize, 0); }
+int LZ4_compressHC2_withStateHC (void* state, const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); }
+int LZ4_compressHC2_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, maxDstSize, cLevel); }
+int LZ4_compressHC_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, LZ4_compressBound(srcSize)); }
+int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, maxDstSize); }
+
+
+/* Deprecated streaming functions */
+int LZ4_sizeofStreamStateHC(void) { return LZ4_STREAMHCSIZE; }
+
+int LZ4_resetStreamStateHC(void* state, char* inputBuffer)
+{
+ LZ4HC_CCtx_internal *ctx = &((LZ4_streamHC_t*)state)->internal_donotuse;
+ if ((((size_t)state) & (sizeof(void*)-1)) != 0) return 1; /* Error : pointer is not aligned for pointer (32 or 64 bits) */
+ LZ4HC_init(ctx, (const BYTE*)inputBuffer);
+ ctx->inputBuffer = (BYTE*)inputBuffer;
+ return 0;
+}
+
+void* LZ4_createHC (char* inputBuffer)
+{
+ LZ4_streamHC_t* hc4 = (LZ4_streamHC_t*)ALLOCATOR(1, sizeof(LZ4_streamHC_t));
+ if (hc4 == NULL) return NULL; /* not enough memory */
+ LZ4HC_init (&hc4->internal_donotuse, (const BYTE*)inputBuffer);
+ hc4->internal_donotuse.inputBuffer = (BYTE*)inputBuffer;
+ return hc4;
+}
+
+int LZ4_freeHC (void* LZ4HC_Data) { FREEMEM(LZ4HC_Data); return 0; }
+
+int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int cLevel)
+{
+ return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, src, dst, &srcSize, 0, cLevel, noLimit);
+}
+
+int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int dstCapacity, int cLevel)
+{
+ return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, src, dst, &srcSize, dstCapacity, cLevel, limitedOutput);
+}
+
+char* LZ4_slideInputBufferHC(void* LZ4HC_Data)
+{
+ LZ4HC_CCtx_internal* const hc4 = &((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse;
+ int const dictSize = LZ4_saveDictHC((LZ4_streamHC_t*)LZ4HC_Data, (char*)(hc4->inputBuffer), 64 KB);
+ return (char*)(hc4->inputBuffer + dictSize);
+}
diff --git a/tools/rasm/minilib.h b/tools/rasm/minilib.h
new file mode 100644
index 0000000..99d55eb
--- /dev/null
+++ b/tools/rasm/minilib.h
@@ -0,0 +1,1151 @@
+#define __FILENAME__ "minilib.h"
+
+#include<string.h>
+#include<stdlib.h>
+#include<stddef.h>
+#include<stdint.h>
+#include<stdarg.h>
+#include<stdio.h>
+#include<errno.h>
+#include<ctype.h>
+#include<time.h>
+#include<math.h>
+#include<sys/types.h>
+#include<sys/stat.h>
+#include<fcntl.h>
+#include<errno.h>
+#ifdef OS_WIN
+#include<io.h>
+#include<limits.h>
+#include<direct.h>
+#define snprintf _snprintf
+#endif
+#include<ctype.h>
+#include<stdio.h>
+#include<stdlib.h>
+#ifndef OS_WIN
+#include<unistd.h>
+#endif
+#include<string.h>
+#include<setjmp.h>
+#include<errno.h>
+#include<sys/timeb.h>
+
+#ifndef ARG_MAX
+#define ARG_MAX 8191
+#endif
+
+
+
+#define MemFree free
+#define MemRealloc realloc
+#define MemMalloc malloc
+#define MemMove memmove
+#ifdef OS_WIN
+#define TxtStrDup _strdup
+#else
+#define TxtStrDup strdup
+#endif
+
+#define loginfo(...); {printf(__VA_ARGS__);printf("\n");}
+#define logdebug(...); {printf(__VA_ARGS__);printf("\n");}
+#define logwarn(...); {printf(__VA_ARGS__);printf("\n");}
+#define logerr(...); {printf(__VA_ARGS__);printf("\n");}
+
+#define INTERNAL_ERROR 1
+#define ABORT_ERROR 7
+
+#define MAX_LINE_BUFFER 16384
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+static int _static_library_nbfile_opened=0;
+static int _static_library_nbfile_opened_max=0;
+
+void _internal_ObjectArrayAddDynamicValue(void **zearray, void *zeobject, int object_size,int curline, char *curfunc, char *cursource);
+void _internal_ObjectArrayAddDynamicValueConcat(void **zearray, int *nbfields, int *maxfields, void *zeobject, int object_size, int curline, char *curfunc, char *cursource);
+#define ObjectArrayAddDynamicValue(zearray,zeobject,objsize) _internal_ObjectArrayAddDynamicValue(zearray,zeobject,objsize,__LINE__,FUNC,__FILENAME__)
+#define ObjectArrayAddDynamicValueConcat(zearray,nbv,maxv,zeobject,objsize) _internal_ObjectArrayAddDynamicValueConcat(zearray,nbv,maxv,zeobject,objsize,__LINE__,FUNC,__FILENAME__)
+void _internal_IntArrayAddDynamicValueConcat(int **zearray, int *nbval, int *maxval, int zevalue, int curline, char *curfunc, char *cursource);
+#define IntArrayAddDynamicValueConcat(zearray,nbv,maxv,zevalue) _internal_IntArrayAddDynamicValueConcat(zearray,nbv,maxv,zevalue,__LINE__,FUNC,__FILENAME__)
+void _internal_FieldArrayAddDynamicValue(char ***zearray, char *zevalue, int curline, char *curfunc, char *cursource);
+#define FieldArrayAddDynamicValue(zearray,zevalue) _internal_FieldArrayAddDynamicValue(zearray,zevalue,__LINE__,FUNC,__FILENAME__)
+void _internal_FieldArrayAddDynamicValueConcat(char ***zearray, int *nbfields, int *maxfields, char *zevalue, int curline, char *curfunc, char *cursource);
+#define FieldArrayAddDynamicValueConcat(zearray,nb,maxf,zevalue) _internal_FieldArrayAddDynamicValueConcat(zearray,nb,maxf,zevalue,__LINE__,FUNC,__FILENAME__)
+
+
+long long FileGetSize(char *filename);
+
+int MinMaxInt(int zeval, int zemin, int zemax)
+{
+ #undef FUNC
+ #define FUNC "MinMaxInt"
+
+ if (zeval<zemin) return zemin;
+ if (zeval>zemax) return zemax;
+ return zeval;
+}
+
+/* (c) FSF */
+#ifdef __WATCOMC__
+size_t strnlen (s, maxlen)
+ register const char *s;
+ size_t maxlen;
+{
+ register const char *e;
+ size_t n;
+
+ for (e = s, n = 0; *e && n < maxlen; e++, n++)
+ ;
+ return n;
+}
+#endif
+
+
+char *TxtStrDupLen(char *str, int *len)
+{
+ #undef FUNC
+ #define FUNC "TxtStrDupLen"
+
+ char *newstr;
+ *len=strlen(str)+1;
+ newstr=MemMalloc(*len);
+ strcpy(newstr,str);
+ return newstr;
+}
+
+void _internal_ObjectArrayAddDynamicValueConcat(void **zearray, int *nbfields, int *maxfields, void *zeobject, int object_size, int curline, char *curfunc, char *cursource)
+{
+ #undef FUNC
+ #define FUNC "ObjectArrayAddDynamicValueConcat"
+
+ char *dst;
+
+ if ((*zearray)==NULL) {
+ *nbfields=1;
+ *maxfields=3;
+ (*zearray)=malloc((*maxfields)*object_size);
+ } else {
+ *nbfields=(*nbfields)+1;
+ if (*nbfields>=*maxfields) {
+ *maxfields=(*maxfields)*2;
+ (*zearray)=realloc((*zearray),(*maxfields)*object_size);
+ }
+ }
+ /* using direct calls because it is more interresting to know which is the caller */
+ dst=((char *)(*zearray))+((*nbfields)-1)*object_size;
+ /* control of memory for overflow */
+ memcpy(dst,zeobject,object_size);
+}
+
+/***
+ Add a value to a list of integers
+ Reallocate memory on the fly
+
+ zearray: pointer to the array of integers
+ nbval: pointer to the current index
+ maxval: pointer to the current allocated memory
+ zevalue: integer value to concat to
+
+ output: the pointer to the array is modified because the
+ function reallocate memory to store the new string at
+ the end of the array.
+*/
+void _internal_IntArrayAddDynamicValueConcat(int **zearray, int *nbval, int *maxval, int zevalue, int curline, char *curfunc, char *cursource)
+{
+ #undef FUNC
+ #define FUNC "IntArrayAddDynamicValue"
+
+ if ((*zearray)==NULL) {
+ *nbval=1;
+ *maxval=4;
+ (*zearray)=MemMalloc(sizeof(int)*(*maxval));
+ } else {
+ *nbval=*nbval+1;
+ if (*nbval>*maxval) {
+ *maxval=(*maxval)*2;
+ (*zearray)=realloc((*zearray),sizeof(int)*(*maxval));
+ }
+ }
+ (*zearray)[(*nbval)-1]=zevalue;
+}
+
+/***
+ CSVGetFieldArrayNumber
+
+ count array elements
+*/
+int CSVGetFieldArrayNumber(char **myfield)
+{
+ #undef FUNC
+ #define FUNC "CSVGetFieldArrayNumber"
+
+ int n=0;
+
+ if (myfield==NULL)
+ return 0;
+
+ while (myfield[n]!=NULL) n++;
+
+ return n;
+
+}
+/***
+ * TxtSplit
+ *
+ * split the parameter string into an array of strings
+ */
+char **TxtSplitWithChar(char *in_str, char split_char)
+{
+ #undef FUNC
+ #define FUNC "TxtSplitWithChar"
+
+ char **tab=NULL;
+ char *match_str;
+ int redo=1;
+ int idx,idmax;
+
+ match_str=in_str;
+
+ while (redo && *in_str) {
+ while (*match_str && *match_str!=split_char) match_str++;
+ redo=*match_str;
+ *match_str=0;
+ FieldArrayAddDynamicValueConcat(&tab,&idx,&idmax,in_str);
+ in_str=++match_str;
+ }
+
+ return tab;
+}
+
+/***
+ Add a value to a list of strings
+ Reallocate memory on the fly
+
+ input: pointer to an array of string
+ string value
+
+ output: the pointer to the array is modified because the
+ function reallocate memory to store the new string at
+ the end of the array.
+*/
+void _internal_FieldArrayAddDynamicValue(char ***zearray, char *zevalue, int curline, char *curfunc, char *cursource)
+{
+ #undef FUNC
+ #define FUNC "FieldArrayAddDynamicValue"
+ int nbfield;
+ if ((*zearray)==NULL) nbfield=2; else nbfield=CSVGetFieldArrayNumber((*zearray))+2;
+ /* using direct calls because it is more interresting to know which is the caller */
+ (*zearray)=realloc((*zearray),nbfield*sizeof(char *));
+ (*zearray)[nbfield-2]=TxtStrDup(zevalue);
+ (*zearray)[nbfield-1]=NULL;
+}
+void _internal_FieldArrayAddDynamicValueConcat(char ***zearray, int *nbfields, int *maxfields, char *zevalue, int curline, char *curfunc, char *cursource)
+{
+ #undef FUNC
+ #define FUNC "FieldArrayAddDynamicValueConcat"
+
+ if ((*zearray)==NULL) {
+ *nbfields=1;
+ *maxfields=10;
+ (*zearray)=realloc(NULL,(*maxfields)*sizeof(char *));
+ } else {
+ *nbfields=(*nbfields)+1;
+ if (*nbfields>=*maxfields) {
+ *maxfields=(*maxfields)*2;
+ (*zearray)=MemRealloc((*zearray),(*maxfields)*sizeof(char *));
+ }
+ }
+ /* using direct calls because it is more interresting to know which is the caller */
+ (*zearray)[(*nbfields)-1]=TxtStrDup(zevalue);
+ (*zearray)[(*nbfields)]=NULL;
+}
+
+
+
+/***
+ CSVFreeFields
+
+ free allocated memory for fields
+*/
+void CSVFreeFields(char **fields)
+{
+ #undef FUNC
+ #define FUNC "CSVFreeFields"
+
+ int i=0;
+ if (fields!=NULL)
+ {
+ /*loginfo("%8X",fields);
+ if (fields[i]==NULL)
+ loginfo("NULL i=%d",i); */
+
+ while (fields[i]!=NULL)
+ {
+ MemFree(fields[i]);
+ i++;
+ }
+ MemFree(fields);
+ }
+}
+void FreeArrayDynamicValue(char ***zearray)
+{
+ CSVFreeFields(*zearray);
+ *zearray=NULL;
+}
+
+
+void CSVFreeFields(char **fields);
+#define FreeFields(fields) CSVFreeFields(fields)
+
+
+/************************** File operation ******************************/
+
+
+/***
+ s_fileid
+ structure used by FileReadLine and FileWriteLine to manage multiple files at a time
+*/
+struct s_fileid
+{
+ FILE *file_id;
+ char *filename;
+ char opening_type[4];
+ int cpt;
+ struct s_fileid *next;
+ /* v2 */
+ int closed;
+ unsigned long curpos;
+};
+
+static struct s_fileid *fileidROOT=NULL;
+
+/***
+ FileGetStructFromName
+
+ input: filename
+ output: file structure
+ NULL if not found
+*/
+struct s_fileid *FileGetStructFromName(char *filename)
+{
+ #undef FUNC
+ #define FUNC "FileGetStructFromName"
+ struct s_fileid *curfile;
+ struct s_fileid *prevfile;
+
+ if (!filename)
+ {
+ logerr("filename must not be NULL");
+ exit(ABORT_ERROR);
+ }
+
+ if (strnlen(filename,PATH_MAX)==PATH_MAX)
+ {
+ logerr("cannot open this file because the argument size is bigger than PATH_MAX (%d)",PATH_MAX);
+ exit(ABORT_ERROR);
+ }
+
+ /* try to find the file in the list */
+ curfile=fileidROOT;
+ prevfile=NULL;
+ while (curfile!=NULL)
+ {
+ if (!strcmp(curfile->filename,filename)) {
+ break;
+ } else {
+ prevfile=curfile;
+ curfile=curfile->next;
+ }
+ }
+ /* put the struct to the top of the list */
+ if (curfile && prevfile) {
+ prevfile->next=curfile->next;
+ curfile->next=fileidROOT;
+ fileidROOT=curfile;
+ }
+
+ return curfile;
+}
+
+/***
+ FileOpen function
+
+ open a file in any mode (r,w,a,r+,w+,a+)
+ check if the file is already open
+ check for conflicts
+*/
+FILE *FileOpen(char *filename, char *opening_type)
+{
+ #undef FUNC
+ #define FUNC "FileOpen"
+ struct s_fileid *curfile;
+ struct s_fileid *oldfile;
+
+ /* check parameters coherency */
+ if (strlen(opening_type)>3)
+ {
+ logerr("illegal opening type (too long)");
+ exit(ABORT_ERROR);
+ }
+ if (strcmp(opening_type,"a") && strcmp(opening_type,"w") && strcmp(opening_type,"r")
+ && strcmp(opening_type,"a+") && strcmp(opening_type,"w+") && strcmp(opening_type,"r+") && strcmp(opening_type,"rb"))
+ {
+ logerr("illegal opening type [%s]\nallowed options are: r,w,a,r+,w+,a+",opening_type);
+ exit(ABORT_ERROR);
+ }
+
+ curfile=FileGetStructFromName(filename);
+
+ /* if curfile is NULL then the file is not opened yet */
+ if (!curfile)
+ {
+ /* insert a new record */
+ _static_library_nbfile_opened++;
+ if (_static_library_nbfile_opened>_static_library_nbfile_opened_max)
+ _static_library_nbfile_opened_max=_static_library_nbfile_opened;
+ curfile=MemMalloc(sizeof(struct s_fileid));
+ memset(curfile,0,sizeof(struct s_fileid));
+ curfile->filename=MemMalloc(strlen(filename)+1);
+ strcpy(curfile->filename,filename);
+ strcpy(curfile->opening_type,opening_type);
+ curfile->next=fileidROOT;
+ curfile->cpt=0;
+ fileidROOT=curfile;
+ }
+ else
+ {
+ if (strcmp(curfile->opening_type,opening_type))
+ {
+ logerr("You can't open the file [%s] in [%s] mode cause it's already open in [%s] mode",filename,opening_type,curfile->opening_type);
+ exit(ABORT_ERROR);
+ }
+ if (!curfile->closed) {
+ /* already opened, just return the id */
+ return curfile->file_id;
+ }
+ }
+
+ curfile->file_id=fopen(filename,opening_type);
+ if (!curfile->file_id)
+ {
+ if (errno==EMFILE) {
+ /* too many files opened, close the latest of the list */
+ oldfile=fileidROOT;
+ while (oldfile->next && !oldfile->next->closed) oldfile=oldfile->next;
+ if (oldfile==curfile) {
+ logerr("cannot open a single file!");
+ exit(INTERNAL_ERROR);
+ }
+ /* save position and close the file */
+ oldfile->curpos=ftell(oldfile->file_id);
+ fclose(oldfile->file_id);
+ oldfile->file_id=0;
+ oldfile->closed=1;
+ /* then try again */
+ curfile->file_id=fopen(filename,opening_type);
+ if (curfile->file_id) {
+ if (curfile->curpos) {
+ fseek(curfile->file_id,curfile->curpos,SEEK_SET);
+ curfile->curpos=0;
+ }
+
+ logdebug("opening file [%s] in %s mode (too many opened files limit reached)",filename,opening_type);
+ return curfile->file_id;
+ }
+ }
+
+ logerr("failed to open file [%s] with mode [%s]",filename,opening_type);
+ logerr("check empty space and permissions");
+ exit(ABORT_ERROR);
+ }
+
+ /* go on previous position */
+ if (curfile->curpos) {
+ fseek(curfile->file_id,curfile->curpos,SEEK_SET);
+ curfile->curpos=0;
+ }
+
+ //logdebug("opening file [%s] in %s mode",filename,opening_type);
+ return curfile->file_id;
+}
+
+struct s_fileid *FileGetStructFromID(FILE *file_id);
+
+void FileSeekBinary(char *filename,int pos, int st)
+{
+ #undef FUNC
+ #define FUNC "FileSeekBinary"
+ FILE *last_id=NULL;
+ struct s_fileid *curfile=NULL;
+ int err;
+
+ last_id=FileOpen(filename,"r");
+ if ((err=fseek(last_id,pos,st))!=0) {
+ logerr("error seeking %s (%d)",filename,ferror(last_id));
+ exit(ABORT_ERROR);
+ }
+
+ curfile=FileGetStructFromID(last_id);
+ switch (st) {
+ case SEEK_SET: curfile->curpos=pos; break;
+ case SEEK_CUR: curfile->curpos+=pos; break;
+ case SEEK_END: logerr("TODO§§§"); break;
+ default:logerr("unknown SEEK mode!");exit(INTERNAL_ERROR);
+ }
+}
+
+/***
+ FileGetStructFromID
+
+ retrieve the file structure from the tree, with his ID
+*/
+struct s_fileid *FileGetStructFromID(FILE *file_id)
+{
+ #undef FUNC
+ #define FUNC "FileGetStructFromID"
+ struct s_fileid *curfile;
+ struct s_fileid *prevfile;
+
+ curfile=fileidROOT;
+ prevfile=NULL;
+ while (curfile!=NULL)
+ {
+ if (curfile->file_id==file_id) {
+ break;
+ } else {
+ prevfile=curfile;
+ curfile=curfile->next;
+ }
+ }
+ if (!curfile)
+ {
+ logerr("ID requested for an unknown file! (was supposed to be opened)");
+ exit(INTERNAL_ERROR);
+ }
+ /* put the struct to the top of the list */
+ if (prevfile) {
+ prevfile->next=curfile->next;
+ curfile->next=fileidROOT;
+ fileidROOT=curfile;
+ }
+
+ return curfile;
+}
+
+/***
+ FileClose function
+
+ check for closing return code
+ free the memory file structure
+*/
+void FileClose(FILE *file_id)
+{
+ #undef FUNC
+ #define FUNC "FileClose"
+ struct s_fileid *curfile;
+ struct s_fileid *unlinkcell;
+
+ curfile=FileGetStructFromID(file_id);
+
+ if (!curfile->closed) {
+ if (fclose(curfile->file_id))
+ {
+ logerr("error while closing file [%s]",curfile->filename);
+ }
+ } else {
+ /* already closed */
+ }
+
+ /* unlink the cell from ROOT */
+ if (curfile==fileidROOT)
+ {
+ fileidROOT=curfile->next;
+ }
+ else
+ {
+ unlinkcell=fileidROOT;
+ while (unlinkcell->next!=curfile)
+ unlinkcell=unlinkcell->next;
+ unlinkcell->next=curfile->next;
+ }
+ MemFree(curfile->filename);
+ MemFree(curfile);
+ _static_library_nbfile_opened--;
+}
+
+/***
+ FileAddCPT
+
+ add n to counter when reading or writing in a file
+*/
+void FileAddCPT(FILE *file_id, int n)
+{
+ #undef FUNC
+ #define FUNC "FileAddCPT"
+ struct s_fileid *curfile;
+
+ curfile=FileGetStructFromID(file_id);
+ curfile->cpt+=n;
+}
+#define FileIncCPT(file_id) FileAddCPT(file_id,1)
+
+/***
+ FileGetCPT
+
+ Get file counter information
+ input: file_id
+*/
+int FileGetCPT(FILE *file_id)
+{
+ #undef FUNC
+ #define FUNC "FileGetCPT"
+ struct s_fileid *curfile;
+
+ curfile=FileGetStructFromID(file_id);
+ return curfile->cpt;
+}
+/***
+ FileGetCPTFromName
+
+ Get file counter information
+ input: filename
+*/
+int FileGetCPTFromName(char *filename)
+{
+ #undef FUNC
+ #define FUNC "FileGetCPTFromName"
+ struct s_fileid *curfile;
+
+ curfile=FileGetStructFromName(filename);
+ if (!curfile)
+ {
+ logerr("You requested a counter for a file that is not opened! [%s]",filename);
+ exit(INTERNAL_ERROR);
+ }
+ return curfile->cpt;
+}
+
+
+
+char *_internal_fgetsClose(char *buffer, int maxlen, FILE *f)
+{
+ #undef FUNC
+ #define FUNC "_internal_fgetsClose"
+ return NULL;
+}
+
+/***
+ FileReadLine/FileReadLineXML function
+
+ input:
+ - filename
+
+ output:
+ - a static buffer with the line read
+
+ this function can handle many file simultaneously
+ just use different filenames without regarding to opened/closed pointer
+ the opened handles are automatically closed when the end of the file is reached
+ you are only limited by the system, not by the code
+
+ the XML version ass carriage returns after closing tag
+ this can be usefull for parsing when reading XML file in a single line
+*/
+enum e_reading_mode {
+RAW_READING,
+CLOSE_READING
+};
+
+char *_internal_fgetsmulti(char *filename, int read_mode)
+{
+ #undef FUNC
+ #define FUNC "_internal_fgetsmulti"
+ static char buffer[MAX_LINE_BUFFER+1]={0};
+ FILE *last_id=NULL;
+ char * (*_file_get_string)(char *, int, FILE *);
+
+ last_id=FileOpen(filename,"r");
+
+ switch (read_mode)
+ {
+ case RAW_READING:_file_get_string=fgets;break;
+ case CLOSE_READING:_file_get_string=_internal_fgetsClose;break;
+ default:logerr("Unknown read mode! (%d)",read_mode);
+ }
+
+ if (_file_get_string(buffer,MAX_LINE_BUFFER,last_id)!=NULL)
+ {
+ FileIncCPT(last_id);
+ if (strnlen(buffer,MAX_LINE_BUFFER)==MAX_LINE_BUFFER)
+ {
+ logerr("line %d is too long! More than %d characters\n",FileGetCPT(last_id),MAX_LINE_BUFFER);
+ exit(INTERNAL_ERROR);
+ }
+ return buffer;
+ }
+ else
+ {
+ /* End of file, we close the handle */
+ logdebug("%d line(s) read from %s",FileGetCPT(last_id),filename);
+ FileClose(last_id);
+ return NULL;
+ }
+}
+
+char **_internal_fgetsmultilines(char *filename, int read_mode)
+{
+ #undef FUNC
+ #define FUNC "_internal_fgetsmultilines"
+ static char buffer[MAX_LINE_BUFFER+1]={0};
+ FILE *last_id=NULL;
+ char * (*_file_get_string)(char *, int, FILE *);
+ char **lines_buffer=NULL;
+ int cur_line=0,max_line=0;
+
+ last_id=FileOpen(filename,"r");
+
+ switch (read_mode)
+ {
+ case RAW_READING:_file_get_string=fgets;break;
+ case CLOSE_READING:_file_get_string=_internal_fgetsClose;break;
+ default:logerr("Unknown read mode! (%d)",read_mode);
+ }
+
+
+ while (_file_get_string(buffer,MAX_LINE_BUFFER,last_id)!=NULL)
+ {
+ FileIncCPT(last_id);
+ if (strnlen(buffer,MAX_LINE_BUFFER)==MAX_LINE_BUFFER)
+ {
+ logerr("line %d is too long! More than %d characters\n",FileGetCPT(last_id),MAX_LINE_BUFFER);
+ exit(INTERNAL_ERROR);
+ }
+ FieldArrayAddDynamicValueConcat(&lines_buffer,&cur_line,&max_line,buffer);
+ }
+ /* if file is empty, we allocate an empty struct */
+ if (!lines_buffer) {
+ /* create the end of the list */
+ lines_buffer=MemMalloc(sizeof(char*));
+ lines_buffer[0]=NULL;
+ }
+
+ /* End of file, we close the handle */
+ logdebug("%d line(s) read from %s",FileGetCPT(last_id),filename);
+ FileClose(last_id);
+
+ return lines_buffer;
+}
+
+#define FileReadClose(filename) _internal_fgetsmulti(filename,CLOSE_READING)
+#define FileReadLine(filename) _internal_fgetsmulti(filename,RAW_READING)
+
+long FileReadPos(char *filename)
+{
+ #undef FUNC
+ #define FUNC "FileReadPos"
+
+ FILE *last_id;
+
+ last_id=FileOpen(filename,"r");
+ return ftell(last_id);
+}
+
+/***
+
+ FileWriteLine function
+
+ input:
+ - filename
+ - a buffer with the line to write
+
+ the function do not close the file until a NULL buffer is sent.
+ It prevents from too much write flushes
+
+ you can use it with many files simultaneously but do not forget
+ to close your files if you use this function in a loop of filenames
+ or the system will warn you
+*/
+
+void FileWriteLine(char *filename, char *buffer)
+{
+ #undef FUNC
+ #define FUNC "FileWriteLine"
+ FILE *last_id=NULL;
+
+ last_id=FileOpen(filename,"a+");
+
+ if (buffer!=NULL)
+ {
+ fputs(buffer,last_id);
+ FileIncCPT(last_id);
+ }
+ else
+ {
+ /* NULL buffer sent, this means End of file, we close the handle */
+ //logdebug("%d line(s) written to %s",FileGetCPT(last_id),filename);
+ FileClose(last_id);
+ }
+}
+void FileWriteLines(char *filename, char **buffer)
+{
+ #undef FUNC
+ #define FUNC "FileWriteLines"
+ FILE *last_id=NULL;
+ int i;
+
+ last_id=FileOpen(filename,"a+");
+ for (i=0;buffer[i];i++) {
+ fputs(buffer[i],last_id);
+ FileIncCPT(last_id);
+ }
+ //logdebug("%d line(s) written to %s",FileGetCPT(last_id),filename);
+ FileClose(last_id);
+}
+
+/***
+ FileReadBinary function
+
+ read n bytes into buffer
+ return number of byte really read
+
+ as other File functions, you can use it with many files simultaneously
+*/
+int FileReadBinary(char *filename,char *data,int n)
+{
+ #undef FUNC
+ #define FUNC "FileReadBinary"
+ FILE *last_id=NULL;
+ int nn;
+
+ last_id=FileOpen(filename,"rb");
+
+ if (data==NULL)
+ {
+ FileClose(last_id);
+ return 0;
+ }
+
+ nn=fread(data,1,n,last_id);
+ //printf("%d bytes lus\n",nn);
+ FileAddCPT(last_id,nn);
+ if (nn!=n)
+ {
+ /* if eof is set, this is not an error */
+ if (feof(last_id))
+ {
+ //logdebug("%d byte(s) read from %s",FileGetCPT(last_id),filename);
+ }
+ else
+ if (ferror(last_id))
+ {
+ if (!nn) {
+ logerr("cannot read %s",filename);
+ }
+ else {
+ logerr("error %d bytes were read during reading of %d bytes of %s",nn,n,filename);
+ }
+ exit(ABORT_ERROR);
+ }
+ else
+ {
+ logerr("error during read of %s (but no error neither eof flag set)",filename);
+ exit(INTERNAL_ERROR);
+ }
+ FileClose(last_id);
+ }
+ return nn;
+}
+
+/***
+ FileWriteBinary function
+
+ write n bytes from buffer to file
+ return number of byte really written
+
+ as other File functions, you can use it with many files simultaneously
+*/
+int FileWriteBinary(char *filename,char *data,int n)
+{
+ #undef FUNC
+ #define FUNC "FileWriteBinary"
+ FILE *last_id=NULL;
+ int nn;
+
+ #ifdef OS_WIN
+ last_id=FileOpen(filename,"w");
+ #else
+ last_id=FileOpen(filename,"a+");
+ #endif
+ if (data!=NULL)
+ {
+ #ifdef OS_WIN
+ int sr;
+ sr=_setmode(_fileno(last_id), _O_BINARY );
+ if (sr==-1) {
+ logerr("FATAL: cannot set binary mode for writing");
+ exit(ABORT_ERROR);
+ }
+ #endif
+ nn=fwrite(data,1,n,last_id);
+ FileAddCPT(last_id,nn);
+ /* we must always write the same amount of data */
+ if (n!=nn)
+ {
+ if (ferror(last_id))
+ {
+ if (!nn) {
+ logerr("cannot write %s",filename);
+ logerr("%s",strerror(errno));
+ }
+ else{
+ logerr("error during write of %s (%d byte(s))",filename,FileGetCPT(last_id));
+ }
+ exit(ABORT_ERROR);
+ }
+ else
+ {
+ logerr("error during write of %s (but no error neither eof flag set) %d byte(s) written",filename,FileGetCPT(last_id));
+ exit(INTERNAL_ERROR);
+ }
+ }
+ }
+ else
+ {
+ /* NULL buffer sent, this means End of file, we close the handle */
+ //logdebug("%d byte(s) written to %s",FileGetCPT(last_id),filename);
+ FileClose(last_id);
+ }
+ return nn;
+}
+/*** macro to close binary files */
+#define FileWriteLineClose(filename) FileWriteLine(filename,NULL)
+#define FileReadBinaryClose(filename) FileReadBinary(filename,NULL,0)
+#define FileWriteBinaryClose(filename) FileWriteBinary(filename,NULL,0)
+
+/***
+ FileGetStat
+
+ input: filename
+ return the stat structure of a given file
+*/
+struct stat *FileGetStat(char *filename)
+{
+ #undef FUNC
+ #define FUNC "FileGetStat"
+ struct stat *filestat;
+#ifdef OS_WIN
+ struct _stat winstat;
+#endif
+
+ if (!filename)
+ {
+ logerr("filename must not be NULL");
+ exit(ABORT_ERROR);
+ }
+ /* check after by the system but... */
+ if (strnlen(filename,PATH_MAX)==PATH_MAX)
+ {
+ logerr("cannot open this file because the argument size is bigger than PATH_MAX (%d)",PATH_MAX);
+ logerr("[%s]",filename);
+ exit(ABORT_ERROR);
+ }
+
+ filestat=MemMalloc(sizeof(struct stat));
+ memset(filestat,0,sizeof(struct stat));
+#ifdef OS_WIN
+ if (_stat(filename,&winstat)!=0)
+#else
+ if (stat(filename,filestat) && errno!=ENOENT)
+#endif
+ {
+ logerr("stat %s failed",filename);
+ switch (errno)
+ {
+ case EACCES:logerr("Search permission is denied for one of the directories in the path prefix of path.");break;
+ case EBADF:logerr("filedes is bad.");break;
+ case EFAULT:logerr("Bad address.");break;
+ //case ELOOP:logerr("Too many symbolic links encountered while traversing the path.");break;
+ case ENAMETOOLONG:logerr("File name too long.");break;
+ case ENOMEM:logerr("Out of memory (i.e. kernel memory).");break;
+ case ENOTDIR:logerr("A component of the path is not a directory.");break;
+ default:logerr("Unknown error %d during stat: %s",errno,strerror(errno));
+ }
+ exit(ABORT_ERROR);
+ }
+#ifdef OS_WIN
+ filestat->st_size=winstat.st_size;
+ filestat->st_mode=winstat.st_mode;
+ filestat->st_atime=winstat.st_atime; /* last access */
+ filestat->st_mtime=winstat.st_mtime; /* last modification */
+ filestat->st_ctime=winstat.st_ctime; /* time of creation */
+#endif
+ return filestat;
+}
+
+/***
+ FileGetSize
+
+ input: filename
+ output: the size in bytes of the file
+*/
+long long FileGetSize(char *filename)
+{
+ #undef FUNC
+ #define FUNC "FileGetSize"
+ struct stat *filestat;
+ long long nn;
+
+ filestat=FileGetStat(filename);
+ nn=filestat->st_size;
+ MemFree(filestat);
+ //logdebug("size of %s = %d (%dkb)",filename,nn,nn/1024);
+ return nn;
+}
+
+/***
+ * append a string
+ * return the number of char appened
+ */
+int strappend(char *Adst, char *Asrc)
+{
+ int Lcpt=0;
+
+ /* must be at the end to concat */
+ while (*Adst) {
+ Adst++;
+ }
+
+ /* concat and count */
+ while (*Asrc) {
+ *Adst++=*Asrc++;
+ Lcpt++;
+ }
+ *Adst=0;
+
+ return Lcpt;
+}
+
+char * TxtTrimEnd(char *in_str)
+{
+ #undef FUNC
+ #define FUNC "TxtTrimEnd"
+
+ char *curs;
+ char *space=NULL;
+
+ curs=in_str;
+ /* read the string and save the first space position, unfollowed by another character */
+ while (*curs) {
+ //if (*curs==' ' || *curs==0x0D || *curs==0x0A || *curs=='\t') {
+ /* this include tab, space, line feed, carriage return, ...*/
+ if (*curs<=' ') {
+ if (!space) {
+ space=curs;
+ }
+ } else {
+ space=NULL;
+ }
+ curs++;
+ }
+ if (space) {
+ *space=0;
+ }
+
+ return in_str;
+}
+
+int LookForFile(char *filename, char *dirname)
+{
+ #undef FUNC
+ #define FUNC "LookForFile"
+
+ char fullpath[PATH_MAX+1];
+ int pathlen;
+
+ /* checked by DirReadEntry but it is more convenient to have an error inside this function */
+ if (!filename)
+ {
+ logerr("You must specify at least a filename with the path, or a filename and a dirname. Filename cannot be NULL!");
+ exit(INTERNAL_ERROR);
+ }
+
+ pathlen=strnlen(filename,PATH_MAX);
+ if (pathlen==PATH_MAX)
+ {
+ logerr("cannot look for this file or directory because the argument size is bigger than PATH_MAX (%d)",PATH_MAX);
+ exit(ABORT_ERROR);
+ }
+
+ if (!dirname) {
+#ifdef OS_WIN
+ if (_access(filename,0)==0) return 1; else return 0;
+#else
+ if (access(filename,F_OK)!=-1) return 1; else return 0;
+#endif
+ } else {
+ pathlen+=strnlen(dirname,PATH_MAX-pathlen);
+ if (pathlen>=PATH_MAX-1)
+ {
+ logerr("cannot look for this file or directory because the full path size is bigger than PATH_MAX (%d)",PATH_MAX);
+ exit(ABORT_ERROR);
+ }
+ sprintf(fullpath,"%s/%s",dirname,filename);
+#ifdef OS_WIN
+ if (_access(fullpath,0)==0) return 1; else return 0;
+#else
+ if (access(fullpath,F_OK)!=-1) return 1; else return 0;
+#endif
+ }
+}
+
+#define FileExists(zefile) LookForFile(zefile,NULL)
+
+int _internal_do_remove(char *zename, char *zetype)
+{
+ #undef FUNC
+ #define FUNC "_do_remove"
+
+ if (zename==NULL)
+ {
+ logerr("the argument cannot be NULL");
+ exit(INTERNAL_ERROR);
+ }
+ if (strnlen(zename,PATH_MAX)==PATH_MAX)
+ {
+ logerr("cannot remove this file or directory because the argument size is bigger than PATH_MAX (%d)",PATH_MAX);
+ exit(ABORT_ERROR);
+ }
+
+ if (remove(zename))
+ {
+ switch (errno)
+ {
+ case EACCES:logerr("Write permission is denied for the directory from which the %s [%s] is to be removed, or the directory has the sticky bit set and you do not own the file.",zetype,zename);break;
+ case EBUSY:logerr("This error indicates that the %s [%s] is being used by the system in such a way that it can't be unlinked. For example, you might see this error if the file name specifies the root directory or a mount point for a file system.",zetype,zename);break;
+ case ENOENT:logerr("The %s named [%s] to be deleted doesn't exist.",zetype,zename);break;
+ case EPERM:logerr("On some systems unlink cannot be used to delete the name of a directory [%s], or at least can only be used this way by a privileged user. To avoid such problems, use rmdir to delete directories.",zename);break;
+ case EROFS:logerr("The directory containing the %s named [%s] to be deleted is on a read-only file system and can't be modified.",zetype,zename);break;
+ case ENOTEMPTY:logerr("The directory [%s] to be deleted is not empty.",zename);break;
+ default:logerr("Unknown error %d during remove [%s]: %s",errno,zename,strerror(errno));
+ }
+ exit(ABORT_ERROR);
+ }
+ //logdebug("%s deleted",zename);
+ return 0;
+}
+
+#define FileRemove(filename) _internal_do_remove(filename,"file")
+
+void FileRemoveIfExists(char *filename)
+{
+ #undef FUNC
+ #define FUNC "FileRemoveIfExists"
+ if (FileExists(filename))
+ FileRemove(filename);
+}
+
+
+#undef __FILENAME__
diff --git a/tools/rasm/rasm.h b/tools/rasm/rasm.h
new file mode 100644
index 0000000..211cf9e
--- /dev/null
+++ b/tools/rasm/rasm.h
@@ -0,0 +1,26 @@
+
+struct s_debug_error {
+ char *filename;
+ int line;
+ char *msg;
+ int lenmsg,lenfilename;
+};
+struct s_debug_symbol {
+ char *name;
+ int v;
+};
+struct s_rasm_info {
+ struct s_debug_error *error;
+ int nberror,maxerror;
+ struct s_debug_symbol *symbol;
+ int nbsymbol,maxsymbol;
+};
+
+
+//extern "C" {
+int RasmAssemble(const char *datain, int lenin, unsigned char **dataout, int *lenout);
+int RasmAssembleInfo(const char *datain, int lenin, unsigned char **dataout, int *lenout, struct s_rasm_info **debug);
+void RasmFreeInfoStruct(struct s_rasm_info *debug);
+//};
+
+
diff --git a/tools/rasm/rasm_v0120.c b/tools/rasm/rasm_v0120.c
new file mode 100644
index 0000000..2c0a96a
--- /dev/null
+++ b/tools/rasm/rasm_v0120.c
@@ -0,0 +1,17472 @@
+#define PROGRAM_NAME "RASM"
+#define PROGRAM_VERSION "0.120"
+#define PROGRAM_DATE "xx/12/2019"
+#define PROGRAM_COPYRIGHT "© 2017 BERGE Edouard / roudoudou from Resistance"
+
+#define RASM_VERSION PROGRAM_NAME" v"PROGRAM_VERSION" (build "PROGRAM_DATE")"
+#define RASM_SNAP_VERSION PROGRAM_NAME" v"PROGRAM_VERSION
+
+#define TRACE_GENERALE 0
+#define TRACE_PREPRO 0
+#define TRACE_ASSEMBLE 0
+#define TRACE_COMPUTE_EXPRESSION 0
+#define TRACE_HEXBIN 0
+#define TRACE_MAKEAMSDOSREAL 0
+#define TRACE_STRUCT 0
+#define TRACE_EDSK 0
+
+
+
+/***
+Rasm (roudoudou assembler) Z80 assembler
+
+doc & latest official release at: http://www.cpcwiki.eu/forum/programming/rasm-z80-assembler-in-beta/
+
+You may send requests/bugs in the same topic
+
+-----------------------------------------------------------------------------------------------------
+This software is using MIT "expat" license
+
+« Copyright © BERGE Edouard (roudoudou)
+
+Permission is hereby granted, free of charge,to any person obtaining a copy of this software
+and associated documentation/source files of RASM, 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. »
+-----------------------------------------------------------------------------------------------------
+Linux compilation with GCC or Clang:
+cc rasm_v0116.c -O2 -lm -lrt -march=native -o rasm
+strip rasm
+
+Windows compilation with Visual studio:
+cl.exe rasm_v0116.c -O2 -Ob3
+
+pure MS-DOS 32 bits compilation with Watcom:
+wcl386 rasm_v0116.c -6r -6s -fp6 -d0 -k4000000 -ox /bt=DOS /l=dos4g
+
+MorphOS compilation (ixemul):
+ppc-morphos-gcc-5 -O2 -c -o rasm rasm_v0116.c
+strip rasm
+
+MacOS compilation:
+cc rasm_v0116.c -O2 -lm -march=native -o rasm
+
+*/
+
+#ifdef __WATCOMC__
+#define OS_WIN 1
+#endif
+
+#ifdef _WIN32
+#define OS_WIN 1
+#endif
+
+#ifdef _WIN64
+#define OS_WIN 1
+#endif
+
+#ifndef RDD
+ /* public lib */
+ #include"minilib.h"
+#else
+ /* private dev lib wont be published */
+ #include"../tools/library.h"
+ #define TxtSplitWithChar _internal_TxtSplitWithChar
+#endif
+
+#ifndef NO_3RD_PARTIES
+#define __FILENAME__ "3rd parties"
+/* 3rd parties compression */
+#include"zx7.h"
+#include"lz4.h"
+#include"exomizer.h"
+#endif
+
+#ifdef __MORPHOS__
+/* Add standard version string to executable */
+const char __attribute__((section(".text"))) ver_version[]={ "\0$VER: "PROGRAM_NAME" "PROGRAM_VERSION" ("PROGRAM_DATE") "PROGRAM_COPYRIGHT"" };
+#endif
+
+#undef __FILENAME__
+#define __FILENAME__ "rasm.c"
+
+#ifndef OS_WIN
+#define KNORMAL "\x1B[0m"
+#define KERROR "\x1B[31m"
+#define KAYGREEN "\x1B[32m"
+#define KWARNING "\x1B[33m"
+#define KBLUE "\x1B[34m"
+#define KVERBOSE "\x1B[36m"
+#define KIO "\x1B[97m"
+#else
+#define KNORMAL ""
+#define KERROR "Error: "
+#define KAYGREEN ""
+#define KWARNING "Warning: "
+#define KBLUE ""
+#define KVERBOSE ""
+#define KIO ""
+#endif
+
+/*******************************************************************
+ c o m m a n d l i n e p a r a m e t e r s
+*******************************************************************/
+enum e_dependencies_type {
+E_DEPENDENCIES_NO=0,
+E_DEPENDENCIES_LIST,
+E_DEPENDENCIES_MAKE
+};
+
+struct s_parameter {
+ char **labelfilename;
+ char *filename;
+ char *outputfilename;
+ int automatic_radix;
+ int export_local;
+ int export_var;
+ int export_equ;
+ int export_sym;
+ int export_multisym;
+ int export_tape;
+ char *flexible_export;
+ int export_sna;
+ int export_snabrk;
+ int export_brk;
+ int nowarning;
+ int checkmode;
+ int dependencies;
+ int maxerr;
+ int macrovoid;
+ int extended_error;
+ int edskoverwrite;
+ float rough;
+ int as80,dams;
+ int v2;
+ int warn_unused;
+ char *symbol_name;
+ char *binary_name;
+ char *cartridge_name;
+ char *snapshot_name;
+ char *tape_name;
+ char *breakpoint_name;
+ char **symboldef;
+ int nsymb,msymb;
+ char **pathdef;
+ int npath,mpath;
+};
+
+
+
+/*******************************************************************
+ c o m p u t e o p e r a t i o n s f o r c a l c u l a t o r
+*******************************************************************/
+
+enum e_compute_operation_type {
+E_COMPUTE_OPERATION_PUSH_DATASTC=0,
+E_COMPUTE_OPERATION_OPEN=1,
+E_COMPUTE_OPERATION_CLOSE=2,
+E_COMPUTE_OPERATION_ADD=3,
+E_COMPUTE_OPERATION_SUB=4,
+E_COMPUTE_OPERATION_DIV=5,
+E_COMPUTE_OPERATION_MUL=6,
+E_COMPUTE_OPERATION_AND=7,
+E_COMPUTE_OPERATION_OR=8,
+E_COMPUTE_OPERATION_MOD=9,
+E_COMPUTE_OPERATION_XOR=10,
+E_COMPUTE_OPERATION_NOT=11,
+E_COMPUTE_OPERATION_SHL=12,
+E_COMPUTE_OPERATION_SHR=13,
+E_COMPUTE_OPERATION_BAND=14,
+E_COMPUTE_OPERATION_BOR=15,
+E_COMPUTE_OPERATION_LOWER=16,
+E_COMPUTE_OPERATION_GREATER=17,
+E_COMPUTE_OPERATION_EQUAL=18,
+E_COMPUTE_OPERATION_NOTEQUAL=19,
+E_COMPUTE_OPERATION_LOWEREQ=20,
+E_COMPUTE_OPERATION_GREATEREQ=21,
+/* math functions */
+E_COMPUTE_OPERATION_SIN=22,
+E_COMPUTE_OPERATION_COS=23,
+E_COMPUTE_OPERATION_INT=24,
+E_COMPUTE_OPERATION_FLOOR=25,
+E_COMPUTE_OPERATION_ABS=26,
+E_COMPUTE_OPERATION_LN=27,
+E_COMPUTE_OPERATION_LOG10=28,
+E_COMPUTE_OPERATION_SQRT=29,
+E_COMPUTE_OPERATION_ASIN=30,
+E_COMPUTE_OPERATION_ACOS=31,
+E_COMPUTE_OPERATION_ATAN=32,
+E_COMPUTE_OPERATION_EXP=33,
+E_COMPUTE_OPERATION_LOW=34,
+E_COMPUTE_OPERATION_HIGH=35,
+E_COMPUTE_OPERATION_PSG=36,
+E_COMPUTE_OPERATION_RND=37,
+E_COMPUTE_OPERATION_FRAC=38,
+E_COMPUTE_OPERATION_CEIL=39,
+E_COMPUTE_OPERATION_GET_R=40,
+E_COMPUTE_OPERATION_GET_V=41,
+E_COMPUTE_OPERATION_GET_B=42,
+E_COMPUTE_OPERATION_SET_R=43,
+E_COMPUTE_OPERATION_SET_V=44,
+E_COMPUTE_OPERATION_SET_B=45,
+E_COMPUTE_OPERATION_END=46
+};
+
+struct s_compute_element {
+enum e_compute_operation_type operator;
+double value;
+int priority;
+};
+
+struct s_compute_core_data {
+ /* evaluator v3 may be recursive */
+ char *varbuffer;
+ int maxivar;
+ struct s_compute_element *tokenstack;
+ int maxtokenstack;
+ struct s_compute_element *operatorstack;
+ int maxoperatorstack;
+};
+
+/***********************************************************
+ w a v h e a d e r f o r a u d i o i m p o r t
+***********************************************************/
+struct s_wav_header {
+unsigned char ChunkID[4];
+unsigned char ChunkSize[4];
+unsigned char Format[4];
+unsigned char SubChunk1ID[4];
+unsigned char SubChunk1Size[4];
+unsigned char AudioFormat[2];
+unsigned char NumChannels[2];
+unsigned char SampleRate[4];
+unsigned char ByteRate[4];
+unsigned char BlockAlign[2];
+unsigned char BitsPerSample[2];
+unsigned char SubChunk2ID[4];
+unsigned char SubChunk2Size[4];
+};
+
+enum e_audio_sample_type {
+AUDIOSAMPLE_SMP,
+AUDIOSAMPLE_SM2,
+AUDIOSAMPLE_SM4,
+AUDIOSAMPLE_DMA,
+AUDIOSAMPLE_END
+};
+
+/***********************************************************************
+ e x p r e s s i o n t y p e s f o r d e l a y e d w r i t e
+***********************************************************************/
+enum e_expression {
+ E_EXPRESSION_J8, /* relative 8bits jump */
+ E_EXPRESSION_0V8, /* 8 bits value to current adress */
+ E_EXPRESSION_V8, /* 8 bits value to current adress+1 */
+ E_EXPRESSION_V16, /* 16 bits value to current adress+1 */
+ E_EXPRESSION_V16C, /* 16 bits value to current adress+1 */
+ E_EXPRESSION_0V16, /* 16 bits value to current adress */
+ E_EXPRESSION_0V32, /* 32 bits value to current adress */
+ E_EXPRESSION_0VR, /* AMSDOS real value (5 bytes) to current adress */
+ E_EXPRESSION_IV8, /* 8 bits value to current adress+2 */
+ E_EXPRESSION_IV81, /* 8 bits value+1 to current adress+2 */
+ E_EXPRESSION_3V8, /* 8 bits value to current adress+3 used with LD (IX+n),n */
+ E_EXPRESSION_IV16, /* 16 bits value to current adress+2 */
+ E_EXPRESSION_RST, /* the offset of RST is translated to the opcode */
+ E_EXPRESSION_IM, /* the interrupt mode is translated to the opcode */
+ E_EXPRESSION_RUN, /* delayed RUN value */
+ E_EXPRESSION_ZXRUN, /* delayed RUN value for ZX snapshot */
+ E_EXPRESSION_ZXSTACK,/* delayed STACK value for ZX snapshot */
+ E_EXPRESSION_BRS /* delayed shifting for BIT, RES, SET */
+};
+
+struct s_expression {
+ char *reference; /* backup when used inside loop (or macro?) */
+ int iw; /* word index in the main wordlist */
+ int o; /* offset de depart 0, 1 ou 3 selon l'opcode */
+ int ptr; /* offset courant pour calculs relatifs */
+ int wptr; /* where to write the result */
+ enum e_expression zetype; /* type of delayed write */
+ int lz; /* lz zone */
+ int ibank; /* ibank of expression */
+ int iorgzone; /* org of expression */
+};
+
+struct s_expr_dico {
+ char *name;
+ int crc;
+ int autorise_export;
+ double v;
+ int used;
+ int iw;
+};
+
+struct s_label {
+ char *name; /* is alloced for local repeat or struct OR generated global -> in this case iw=-1 */
+ int iw; /* index of the word of label name */
+ int crc; /* crc of the label name */
+ int ptr; /* "physical" adress */
+ int lz; /* is the label in a crunched section (or after)? */
+ int iorgzone; /* org of label */
+ int ibank; /* current CPR bank / always zero in classic mode */
+ int local;
+ /* errmsg */
+ int fileidx;
+ int fileline;
+ int autorise_export,backidx;
+ int used;
+};
+
+struct s_alias {
+ char *alias;
+ char *translation;
+ int crc,len,autorise_export;
+ int iw;
+ int used;
+};
+
+struct s_ticker {
+ char *varname;
+ int crc;
+ long nopstart;
+ long tickerstart;
+};
+
+/***********************************************************************
+ m e r k e l t r e e s f o r l a b e l, v a r, a l i a s
+***********************************************************************/
+struct s_crclabel_tree {
+ struct s_crclabel_tree *radix[256];
+ struct s_label *label;
+ int nlabel,mlabel;
+};
+struct s_crcdico_tree {
+ struct s_crcdico_tree *radix[256];
+ struct s_expr_dico *dico;
+ int ndico,mdico;
+};
+struct s_crcused_tree {
+ struct s_crcused_tree *radix[256];
+ char **used;
+ int nused,mused;
+};
+struct s_crcstring_tree {
+ struct s_crcstring_tree *radix[256];
+ char **text;
+ int ntext,mtext;
+ char **replace;
+ int nreplace,mreplace;
+};
+/*************************************************
+ m e m o r y s e c t i o n
+*************************************************/
+struct s_lz_section {
+ int iw;
+ int memstart,memend;
+ int lzversion; /* 4 -> LZ4 / 7 -> ZX7 / 48 -> LZ48 / 49 -> LZ49 / 8 -> Exomizer */
+ int iorgzone;
+ int ibank;
+ /* idx backup */
+ int iexpr;
+ int ilabel;
+};
+
+struct s_orgzone {
+ int ibank,protect;
+ int memstart,memend;
+ int ifile,iline;
+ int nocode;
+};
+
+/**************************************************
+ i n c b i n s t o r a g e
+**************************************************/
+struct s_hexbin {
+ unsigned char *data;
+ int datalen,rawlen;
+ char *filename;
+ int crunch;
+};
+
+/**************************************************
+ e d s k m a n a g e m e n t
+**************************************************/
+struct s_edsk_sector_global_struct {
+unsigned char track;
+unsigned char side;
+unsigned char id;
+unsigned char size;
+unsigned char st1;
+unsigned char st2;
+unsigned short int length;
+unsigned char *data;
+};
+
+struct s_edsk_track_global_struct {
+int sectornumber;
+/* information purpose */
+int sectorsize;
+int gap3length;
+int fillerbyte;
+int datarate;
+int recordingmode;
+struct s_edsk_sector_global_struct *sector;
+};
+
+struct s_edsk_global_struct {
+int tracknumber;
+int sidenumber;
+int tracksize; /* DSK legacy */
+struct s_edsk_track_global_struct *track;
+};
+
+struct s_edsk_wrapper_entry {
+unsigned char user;
+unsigned char filename[11];
+unsigned char subcpt;
+unsigned char extendcounter;
+unsigned char reserved;
+unsigned char rc;
+unsigned char blocks[16];
+};
+
+struct s_edsk_wrapper {
+char *edsk_filename;
+struct s_edsk_wrapper_entry entry[64];
+int nbentry;
+unsigned char blocks[178][1024]; /* DATA format */
+int face;
+};
+
+struct s_save {
+ int ibank;
+ int ioffset;
+ int isize;
+ int iw,irun;
+ int amsdos,hobeta;
+ int tape,dsk,face,iwdskname;
+};
+
+
+/********************
+ L O O P S
+********************/
+
+enum e_loop_style {
+E_LOOPSTYLE_REPEATN,
+E_LOOPSTYLE_REPEATUNTIL,
+E_LOOPSTYLE_WHILE
+};
+
+struct s_repeat {
+ int start;
+ int cpt;
+ int value;
+ int maxim;
+ int repeat_counter;
+ char *repeatvar;
+ int repeatcrc;
+};
+
+struct s_whilewend {
+ int start;
+ int cpt;
+ int value;
+ int maxim;
+ int while_counter;
+};
+
+struct s_switchcase {
+ int refval;
+ int execute;
+ int casematch;
+};
+
+struct s_repeat_index {
+ int ifile;
+ int ol,oidx;
+ int cl,cidx;
+};
+
+
+enum e_ifthen_type {
+E_IFTHEN_TYPE_IF=0,
+E_IFTHEN_TYPE_IFNOT=1,
+E_IFTHEN_TYPE_IFDEF=2,
+E_IFTHEN_TYPE_IFNDEF=3,
+E_IFTHEN_TYPE_ELSE=4,
+E_IFTHEN_TYPE_ELSEIF=5,
+E_IFTHEN_TYPE_IFUSED=6,
+E_IFTHEN_TYPE_IFNUSED=7,
+E_IFTHEN_TYPE_END
+};
+
+struct s_ifthen {
+ char *filename;
+ int line,v;
+ enum e_ifthen_type type;
+};
+
+/**************************************************
+ w o r d p r o c e s s i n g
+**************************************************/
+struct s_wordlist {
+ char *w;
+ int l,t,e; /* e=1 si egalite dans le mot */
+ int ifile;
+};
+
+struct s_macro {
+ char *mnemo;
+ int crc;
+ /* une macro concatene des chaines et des parametres */
+ struct s_wordlist *wc;
+ int nbword,maxword;
+ /**/
+ char **param;
+ int nbparam;
+};
+
+struct s_macro_position {
+ int start,end,value;
+};
+
+/* preprocessing only */
+struct s_macro_fast {
+ char *mnemo;
+ int crc;
+};
+
+struct s_math_keyword {
+ char *mnemo;
+ int crc;
+ enum e_compute_operation_type operation;
+};
+
+struct s_expr_word {
+ char *w;
+ int aw;
+ int op;
+ int comma;
+ int fct;
+ double v;
+};
+
+struct s_listing {
+ char *listing;
+ int ifile;
+ int iline;
+};
+
+enum e_tagtranslateoption {
+E_TAGOPTION_NONE=0,
+E_TAGOPTION_REMOVESPACE=1,
+E_TAGOPTION_PRESERVE=2
+};
+
+#ifdef RASM_THREAD
+struct s_rasm_thread {
+ pthread_t thread;
+ int lz;
+ unsigned char *datain;
+ int datalen;
+ unsigned char *dataout;
+ int lenout;
+ int status;
+};
+#endif
+
+
+/*********************************************************
+ S N A P S H O T E X P O R T
+*********************************************************/
+/* extension 4Mo = 256 slots + 4 slots 64K de RAM par défaut => 260 */
+
+#define BANK_MAX_NUMBER 260
+
+struct s_snapshot_symbol {
+ unsigned char size;
+ unsigned char name[256];
+ unsigned char reserved[6];
+ unsigned char bigendian_adress[2];
+};
+
+
+struct s_zxsnapshot {
+
+ unsigned int run;
+ unsigned int stack;
+};
+
+struct s_snapshot {
+ char idmark[8];
+ char unused1[8];
+ unsigned char version; /* 3 */
+ struct {
+ struct {
+ unsigned char F;
+ unsigned char A;
+ unsigned char C;
+ unsigned char B;
+ unsigned char E;
+ unsigned char D;
+ unsigned char L;
+ unsigned char H;
+ }general;
+ unsigned char R;
+ unsigned char regI; /* I incompatible with tgmath.h */
+ unsigned char IFF0;
+ unsigned char IFF1;
+ unsigned char LX;
+ unsigned char HX;
+ unsigned char LY;
+ unsigned char HY;
+ unsigned char LSP;
+ unsigned char HSP;
+ unsigned char LPC;
+ unsigned char HPC;
+ unsigned char IM; /* 0,1,2 */
+ struct {
+ unsigned char F;
+ unsigned char A;
+ unsigned char C;
+ unsigned char B;
+ unsigned char E;
+ unsigned char D;
+ unsigned char L;
+ unsigned char H;
+ }alternate;
+ }registers;
+
+ struct {
+ unsigned char selectedpen;
+ unsigned char palette[17];
+ unsigned char multiconfiguration;
+ }gatearray;
+ unsigned char ramconfiguration;
+ struct {
+ unsigned char selectedregister;
+ unsigned char registervalue[18];
+ }crtc;
+ unsigned char romselect;
+ struct {
+ unsigned char portA;
+ unsigned char portB;
+ unsigned char portC;
+ unsigned char control;
+ }ppi;
+ struct {
+ unsigned char selectedregister;
+ unsigned char registervalue[16];
+ }psg;
+ unsigned char dumpsize[2]; /* 64 then use extended memory chunks */
+
+ unsigned char CPCType; /* 0=464 / 1=664 / 2=6128 / 4=6128+ / 5=464+ / 6=GX4000 */
+ unsigned char interruptnumber;
+ unsigned char multimodebytes[6];
+ unsigned char unused2[0x9C-0x75];
+
+ /* offset #9C */
+ struct {
+ unsigned char motorstate;
+ unsigned char physicaltrack;
+ }fdd;
+ unsigned char unused3[3];
+ unsigned char printerstrobe;
+ unsigned char unused4[2];
+ struct {
+ unsigned char model; /* 0->4 */
+ unsigned char unused5[4];
+ unsigned char HCC;
+ unsigned char unused;
+ unsigned char CLC;
+ unsigned char RLC;
+ unsigned char VTC;
+ unsigned char HSC;
+ unsigned char VSC;
+ unsigned short int flags;
+ }crtcstate;
+ unsigned char vsyncdelay;
+ unsigned char interruptscanlinecounter;
+ unsigned char interruptrequestflag;
+ unsigned char unused6[0xFF-0xB5+1];
+};
+
+struct s_snapshot_chunks {
+ unsigned char chunkname[4]; /* MEM1 -> MEM8 */
+ unsigned int chunksize;
+};
+
+struct s_breakpoint {
+ int address;
+ int bank;
+};
+
+
+/*********************************
+ S T R U C T U R E S
+*********************************/
+enum e_rasmstructfieldtype {
+E_RASMSTRUCTFIELD_BYTE,
+E_RASMSTRUCTFIELD_WORD,
+E_RASMSTRUCTFIELD_LONG,
+E_RASMSTRUCTFIELD_REAL,
+E_RASMSTRUCTFIELD_END
+};
+struct s_rasmstructfield {
+ char *fullname;
+ char *name;
+ int offset;
+ int size;
+ int crc;
+ /* filler */
+ unsigned char *data;
+ int idata,mdata;
+ enum e_rasmstructfieldtype zetype;
+};
+
+struct s_rasmstruct {
+ char *name;
+ int crc;
+ int size;
+ int ptr;
+ int nbelem;
+ /* fields */
+ struct s_rasmstructfield *rasmstructfield;
+ int irasmstructfield,mrasmstructfield;
+};
+
+/*********************************
+ D E B U G
+*********************************/
+struct s_debug_error {
+ char *filename;
+ int line;
+ char *msg;
+ int lenmsg,lenfilename;
+};
+struct s_debug_symbol {
+ char *name;
+ int v;
+};
+struct s_rasm_info {
+ struct s_debug_error *error;
+ int nberror,maxerror;
+ struct s_debug_symbol *symbol;
+ int nbsymbol,maxsymbol;
+};
+
+/*******************************************
+ G L O B A L S T R U C T
+*******************************************/
+struct s_assenv {
+ /* current memory */
+ int maxptr;
+ /* CPR memory */
+ unsigned char **mem;
+ int iwnamebank[BANK_MAX_NUMBER];
+ int nbbank,maxbank;
+ int forcetape,forcezx,forcecpr,forceROM,bankmode,activebank,amsdos,forcesnapshot,packedbank;
+ struct s_snapshot snapshot;
+ struct s_zxsnapshot zxsnapshot;
+ int bankset[BANK_MAX_NUMBER>>2]; /* 64K selected flag */
+ int bankused[BANK_MAX_NUMBER]; /* 16K selected flag */
+ int bankgate[BANK_MAX_NUMBER+1];
+ int setgate[BANK_MAX_NUMBER+1];
+ int rundefined;
+ /* parsing */
+ struct s_wordlist *wl;
+ int nbword;
+ int idx,stage;
+ char *label_filename;
+ int label_line;
+ char **filename;
+ int ifile,maxfile;
+ int nberr,flux;
+ int fastmatch[256];
+ unsigned char charset[256];
+ int maxerr,extended_error,nowarning;
+ /* ORG tracking */
+ int codeadr,outputadr,nocode;
+ int codeadrbackup,outputadrbackup;
+ int minadr,maxadr;
+ struct s_orgzone *orgzone;
+ int io,mo;
+ /* Struct */
+ struct s_rasmstruct *rasmstruct;
+ int irasmstruct,mrasmstruct;
+ int getstruct;
+ int backup_outputadr,backup_codeadr;
+ char *backup_filename;
+ int backup_line;
+ struct s_rasmstruct *rasmstructalias;
+ int irasmstructalias,mrasmstructalias;
+ /* expressions */
+ struct s_expression *expression;
+ int ie,me;
+ int maxam,as80,dams;
+ float rough;
+ struct s_compute_core_data *computectx,ctx1,ctx2;
+ struct s_crcstring_tree stringtree;
+ /* label */
+ struct s_label *label;
+ int il,ml;
+ struct s_crclabel_tree labeltree; /* fast label access */
+ char *module;
+ int modulen;
+ struct s_breakpoint *breakpoint;
+ int ibreakpoint,maxbreakpoint;
+ char *lastgloballabel;
+ char *lastsuperglobal;
+ int lastgloballabellen, lastglobalalloc;
+ /* repeat */
+ struct s_repeat *repeat;
+ int ir,mr;
+ /* while/wend */
+ struct s_whilewend *whilewend;
+ int iw,mw;
+ /* if/then/else */
+ //int *ifthen;
+ struct s_ifthen *ifthen;
+ int ii,mi;
+ /* switch/case */
+ struct s_switchcase *switchcase;
+ int isw,msw;
+ /* expression dictionnary */
+ struct s_expr_dico *dico;
+ int idic,mdic;
+ struct s_crcdico_tree dicotree; /* fast dico access */
+ struct s_crcused_tree usedtree; /* fast used access */
+ /* ticker */
+ struct s_ticker *ticker;
+ int iticker,mticker;
+ long tick,nop;
+ /* crunch section flag */
+ struct s_lz_section *lzsection;
+ int ilz,mlz;
+ int lz,curlz;
+ /* macro */
+ struct s_macro *macro;
+ int imacro,mmacro;
+ int macrovoid;
+ /* labels locaux */
+ int repeatcounter,whilecounter,macrocounter;
+ struct s_macro_position *macropos;
+ int imacropos,mmacropos;
+ /* alias */
+ struct s_alias *alias;
+ int ialias,malias;
+ /* hexbin */
+ struct s_rasm_thread **rasm_thread;
+ int irt,mrt;
+ struct s_hexbin *hexbin;
+ int ih,mh;
+ char **includepath;
+ int ipath,mpath;
+ /* automates */
+ char AutomateExpressionValidCharExtended[256];
+ char AutomateExpressionValidCharFirst[256];
+ char AutomateExpressionValidChar[256];
+ char AutomateExpressionDecision[256];
+ char AutomateValidLabelFirst[256];
+ char AutomateValidLabel[256];
+ char AutomateDigit[256];
+ char AutomateHexa[256];
+ struct s_compute_element AutomateElement[256];
+ unsigned char psgtab[256];
+ unsigned char psgfine[256];
+ /* output */
+ char *outputfilename;
+ int export_sym,export_local,export_multisym;
+ int export_var,export_equ;
+ int export_sna,export_snabrk;
+ int export_brk,export_tape;
+ int autorise_export;
+ char *flexible_export;
+ char *breakpoint_name;
+ char *symbol_name;
+ char *binary_name;
+ char *cartridge_name;
+ char *snapshot_name;
+ struct s_save *save;
+ int nbsave,maxsave;
+ int current_run_idx;
+ struct s_edsk_wrapper *edsk_wrapper;
+ int nbedskwrapper,maxedskwrapper;
+ int edskoverwrite;
+ int checkmode,dependencies;
+ int stop;
+ int warn_unused;
+ /* debug */
+ struct s_rasm_info debug;
+ struct s_rasm_info **retdebug;
+ int debug_total_len;
+};
+
+/*************************************
+ D I R E C T I V E S
+*************************************/
+struct s_asm_keyword {
+ char *mnemo;
+ int crc;
+ void (*makemnemo)(struct s_assenv *ae);
+};
+
+struct s_math_keyword math_keyword[]={
+{"SIN",0,E_COMPUTE_OPERATION_SIN},
+{"COS",0,E_COMPUTE_OPERATION_COS},
+{"INT",0,E_COMPUTE_OPERATION_INT},
+{"ABS",0,E_COMPUTE_OPERATION_ABS},
+{"LN",0,E_COMPUTE_OPERATION_LN},
+{"LOG10",0,E_COMPUTE_OPERATION_LOG10},
+{"SQRT",0,E_COMPUTE_OPERATION_SQRT},
+{"FLOOR",0,E_COMPUTE_OPERATION_FLOOR},
+{"ASIN",0,E_COMPUTE_OPERATION_ASIN},
+{"ACOS",0,E_COMPUTE_OPERATION_ACOS},
+{"ATAN",0,E_COMPUTE_OPERATION_ATAN},
+{"EXP",0,E_COMPUTE_OPERATION_EXP},
+{"LO",0,E_COMPUTE_OPERATION_LOW},
+{"HI",0,E_COMPUTE_OPERATION_HIGH},
+{"PSGVALUE",0,E_COMPUTE_OPERATION_PSG},
+{"RND",0,E_COMPUTE_OPERATION_RND},
+{"FRAC",0,E_COMPUTE_OPERATION_FRAC},
+{"CEIL",0,E_COMPUTE_OPERATION_CEIL},
+{"GETR",0,E_COMPUTE_OPERATION_GET_R},
+{"GETV",0,E_COMPUTE_OPERATION_GET_V},
+{"GETG",0,E_COMPUTE_OPERATION_GET_V},
+{"GETB",0,E_COMPUTE_OPERATION_GET_B},
+{"SETR",0,E_COMPUTE_OPERATION_SET_R},
+{"SETV",0,E_COMPUTE_OPERATION_SET_V},
+{"SETG",0,E_COMPUTE_OPERATION_SET_V},
+{"SETB",0,E_COMPUTE_OPERATION_SET_B},
+{"",0,-1}
+};
+
+#define CRC_SWITCH 0x01AEDE4A
+#define CRC_CASE 0x0826B794
+#define CRC_DEFAULT 0x9A0DAC7D
+#define CRC_BREAK 0xCD364DDD
+#define CRC_ENDSWITCH 0x18E9FB21
+
+#define CRC_ELSEIF 0xE175E230
+#define CRC_ELSE 0x3FF177A1
+#define CRC_ENDIF 0xCD5265DE
+#define CRC_IF 0x4BD52507
+#define CRC_IFDEF 0x4CB29DD6
+#define CRC_UNDEF 0xCCD2FDEA
+#define CRC_IFNDEF 0xD9AD0824
+#define CRC_IFNOT 0x4CCAC9F8
+#define CRC_WHILE 0xBC268FF1
+#define CRC_UNTIL 0xCC12A604
+#define CRC_MEND 0xFFFD899C
+#define CRC_ENDM 0x3FF9559C
+#define CRC_MACRO 0x64AA85EA
+#define CRC_IFUSED 0x91752638
+#define CRC_IFNUSED 0x1B39A886
+
+#define CRC_SIN 0xE1B71962
+#define CRC_COS 0xE077C55D
+
+#define CRC_0 0x7A98A6A8
+#define CRC_1 0x7A98A6A9
+#define CRC_2 0x7A98A6AA
+
+
+#define CRC_NC 0x4BD52B09
+#define CRC_Z 0x7A98A6D2
+#define CRC_NZ 0x4BD52B20
+#define CRC_P 0x7A98A6C8
+#define CRC_PO 0x4BD53717
+#define CRC_PE 0x4BD5370D
+#define CRC_M 0x7A98A6C5
+
+/* 8 bits registers */
+#define CRC_F 0x7A98A6BE
+#define CRC_I 0x7A98A6C1
+#define CRC_R 0x7A98A6CA
+#define CRC_A 0x7A98A6B9
+#define CRC_B 0x7A98A6BA
+#define CRC_C 0x7A98A6BB
+#define CRC_D 0x7A98A6BC
+#define CRC_E 0x7A98A6BD
+#define CRC_H 0x7A98A6C0
+#define CRC_L 0x7A98A6C4
+/* dual naming */
+#define CRC_XH 0x4BD50718
+#define CRC_XL 0x4BD5071C
+#define CRC_YH 0x4BD50519
+#define CRC_YL 0x4BD5051D
+#define CRC_HX 0x4BD52718
+#define CRC_LX 0x4BD52F1C
+#define CRC_HY 0x4BD52719
+#define CRC_LY 0x4BD52F1D
+#define CRC_IXL 0xE19F1765
+#define CRC_IXH 0xE19F1761
+#define CRC_IYL 0xE19F1166
+#define CRC_IYH 0xE19F1162
+
+/* 16 bits registers */
+#define CRC_BC 0x4BD5D2FD
+#define CRC_DE 0x4BD5DF01
+#define CRC_HL 0x4BD5270C
+#define CRC_IX 0x4BD52519
+#define CRC_IY 0x4BD5251A
+#define CRC_SP 0x4BD5311B
+#define CRC_AF 0x4BD5D4FF
+/* memory convention */
+#define CRC_MHL 0xD0765F5D
+#define CRC_MDE 0xD0467D52
+#define CRC_MBC 0xD05E694E
+#define CRC_MIX 0xD072B76A
+#define CRC_MIY 0xD072B16B
+#define CRC_MSP 0xD01A876C
+#define CRC_MC 0xE018210C
+/* struct parsing */
+#define CRC_DEFB 0x37D15389
+#define CRC_DB 0x4BD5DEFE
+#define CRC_DEFW 0x37D1539E
+#define CRC_DW 0x4BD5DF13
+#define CRC_DEFI 0x37D15390
+#define CRC_DEFS 0x37D1539A
+#define CRC_DS 0x4BD5DF0F
+#define CRC_DEFR 0x37D15399
+#define CRC_DR 0x4BD5DF0E
+
+/* struct declaration use special instructions for defines */
+int ICRC_DEFB,ICRC_DEFW,ICRC_DEFI,ICRC_DEFR,ICRC_DEFS,ICRC_DB,ICRC_DW,ICRC_DR,ICRC_DS;
+/* need to pre-declare var */
+extern struct s_asm_keyword instruction[];
+
+/*
+# base=16
+% base=2
+0-9 base=10
+A-Z variable ou fonction (cos, sin, tan, sqr, pow, mod, and, xor, mod, ...)
++*-/&^m| operateur
+*/
+
+#define AutomateExpressionValidCharExtendedDefinition "0123456789.ABCDEFGHIJKLMNOPQRSTUVWXYZ_{}@+-*/~^$#%§<=>|&"
+#define AutomateExpressionValidCharFirstDefinition "#%0123456789.ABCDEFGHIJKLMNOPQRSTUVWXYZ_@${"
+#define AutomateExpressionValidCharDefinition "0123456789.ABCDEFGHIJKLMNOPQRSTUVWXYZ_{}@$"
+#define AutomateValidLabelFirstDefinition ".ABCDEFGHIJKLMNOPQRSTUVWXYZ_@"
+#define AutomateValidLabelDefinition "0123456789.ABCDEFGHIJKLMNOPQRSTUVWXYZ_@{}"
+#define AutomateDigitDefinition ".0123456789"
+#define AutomateHexaDefinition "0123456789ABCDEF"
+
+#ifndef NO_3RD_PARTIES
+unsigned char *LZ4_crunch(unsigned char *data, int zelen, int *retlen){
+ unsigned char *lzdest=NULL;
+ lzdest=MemMalloc(65536);
+ *retlen=LZ4_compress_HC((char*)data,(char*)lzdest,zelen,65536,9);
+ return lzdest;
+}
+#endif
+unsigned char *LZ48_encode_legacy(unsigned char *data, int length, int *retlength);
+#define LZ48_crunch LZ48_encode_legacy
+unsigned char *LZ49_encode_legacy(unsigned char *data, int length, int *retlength);
+#define LZ49_crunch LZ49_encode_legacy
+
+
+/*
+ * optimised reading of text file in one shot
+ */
+unsigned char *_internal_readbinaryfile(char *filename, int *filelength)
+{
+ #undef FUNC
+ #define FUNC "_internal_readbinaryfile"
+
+ unsigned char *binary_data=NULL;
+
+ *filelength=FileGetSize(filename);
+ binary_data=MemMalloc((*filelength)+1);
+ /* we try to read one byte more to close the file just after the read func */
+ if (FileReadBinary(filename,(char*)binary_data,(*filelength)+1)!=*filelength) {
+ logerr("Cannot fully read %s",filename);
+ exit(INTERNAL_ERROR);
+ }
+ return binary_data;
+}
+char **_internal_readtextfile(char *filename, char replacechar)
+{
+ #undef FUNC
+ #define FUNC "_internal_readtextfile"
+
+ char **lines_buffer=NULL;
+ unsigned char *bigbuffer;
+ int nb_lines=0,max_lines=0,i=0,e=0;
+ int file_size;
+
+ bigbuffer=_internal_readbinaryfile(filename,&file_size);
+
+ while (i<file_size) {
+ while (e<file_size && bigbuffer[e]!=0x0A) {
+ /* Windows de meeeeeeeerrrdde... */
+ if (bigbuffer[e]==0x0D) bigbuffer[e]=replacechar;
+ e++;
+ }
+ if (e<file_size) e++;
+ if (nb_lines>=max_lines) {
+ max_lines=max_lines*2+10;
+ lines_buffer=MemRealloc(lines_buffer,(max_lines+1)*sizeof(char **));
+ }
+ lines_buffer[nb_lines]=MemMalloc(e-i+1);
+ memcpy(lines_buffer[nb_lines],bigbuffer+i,e-i);
+ lines_buffer[nb_lines][e-i]=0;
+ if (0)
+ {
+ int yy;
+ for (yy=0;lines_buffer[nb_lines][yy];yy++) {
+ if (lines_buffer[nb_lines][yy]>31) printf("%c",lines_buffer[nb_lines][yy]); else printf("(0x%X)",lines_buffer[nb_lines][yy]);
+ }
+ printf("\n");
+ }
+ nb_lines++;
+ i=e;
+ }
+ if (!max_lines) {
+ lines_buffer=MemMalloc(sizeof(char**));
+ lines_buffer[0]=NULL;
+ } else {
+ lines_buffer[nb_lines]=NULL;
+ }
+ MemFree(bigbuffer);
+ return lines_buffer;
+}
+
+#define FileReadLines(filename) _internal_readtextfile(filename,':')
+#define FileReadLinesRAW(filename) _internal_readtextfile(filename,0x0D)
+#define FileReadContent(filename,filesize) _internal_readbinaryfile(filename,filesize)
+
+
+/***
+ TxtReplace
+
+ input:
+ in_str: string where replace will occur
+ in_substr: substring to look for
+ out_substr: replace substring
+ recurse: loop until no in_substr is found
+
+ note: in_str MUST BE previously mallocated if out_substr is bigger than in_substr
+*/
+#ifndef RDD
+char *TxtReplace(char *in_str, char *in_substr, char *out_substr, int recurse)
+{
+ #undef FUNC
+ #define FUNC "TxtReplace"
+
+ char *str_look,*m1,*m2;
+ char *out_str;
+ int sl,l1,l2,dif,cpt;
+
+ if (in_str==NULL)
+ return NULL;
+
+ sl=strlen(in_str);
+ l1=strlen(in_substr);
+ /* empty string, nothing to do except return empty string */
+ if (!sl || !l1)
+ return in_str;
+
+ l2=strlen(out_substr);
+ dif=l2-l1;
+
+ /* replace string is small or equal in size, we dont realloc */
+ if (dif<=0)
+ {
+ /* we loop while there is a replace to do */
+ str_look=strstr(in_str,in_substr);
+ while (str_look!=NULL)
+ {
+ /* we copy the new string if his len is not null */
+ if (l2)
+ memcpy(str_look,out_substr,l2);
+ /* only if len are different */
+ if (l1!=l2)
+ {
+ /* we move the end of the string byte per byte
+ because memory locations overlap. This is
+ faster than memmove */
+ m1=str_look+l1;
+ m2=str_look+l2;
+ while (*m1!=0)
+ {
+ *m2=*m1;
+ m1++;m2++;
+ }
+ /* we must copy the EOL */
+ *m2=*m1;
+ }
+ /* look for next replace */
+ if (!recurse)
+ str_look=strstr(str_look+l2,in_substr);
+ else
+ str_look=strstr(in_str,in_substr);
+ }
+ out_str=in_str;
+ }
+ else
+ {
+ /* we need to count each replace */
+ cpt=0;
+ str_look=strstr(in_str,in_substr);
+ while (str_look!=NULL)
+ {
+ cpt++;
+ str_look=strstr(str_look+l1,in_substr);
+ }
+ /* is there anything to do? */
+ if (cpt)
+ {
+ /* we realloc to a size that will fit all replaces */
+ out_str=MemRealloc(in_str,sl+1+dif*cpt);
+ str_look=strstr(out_str,in_substr);
+ while (str_look!=NULL && cpt)
+ {
+ /* as the replace string is bigger we
+ have to move memory first from the end */
+ m1=out_str+sl;
+ m2=m1+dif;
+ sl+=dif;
+ while (m1!=str_look+l1-dif)
+ {
+ *m2=*m1;
+ m1--;m2--;
+ }
+ /* then we copy the replace string (can't be NULL in this case) */
+ memcpy(str_look,out_substr,l2);
+
+ /* look for next replace */
+ if (!recurse)
+ str_look=strstr(str_look+l2,in_substr);
+ else
+ str_look=strstr(in_str,in_substr);
+
+ /* to prevent from naughty overlap */
+ cpt--;
+ }
+ if (str_look!=NULL)
+ {
+ printf("INTERNAL ERROR - overlapping replace string (%s/%s), you can't use this one!\n",in_substr,out_substr);
+ exit(ABORT_ERROR);
+ }
+ }
+ else
+ out_str=in_str;
+ }
+ return out_str;
+}
+#endif
+
+#ifndef min
+#define min(a,b) \
+ ({ __typeof__ (a) _a = (a); \
+ __typeof__ (b) _b = (b); \
+ _a < _b ? _a : _b; })
+#endif
+
+/* Levenshtein implementation by TheRayTracer https://gist.github.com/TheRayTracer/2644387 */
+int _internal_LevenshteinDistance(char *s, char *t)
+{
+ int i,j,n,m,*d;
+ int im,jn;
+ int r;
+
+ n=strlen(s)+1;
+ m=strlen(t)+1;
+ d=malloc(n*m*sizeof(int));
+ memset(d, 0, sizeof(int) * n * m);
+
+ for (i = 1, im = 0; i < m; i++, im++)
+ {
+ for (j = 1, jn = 0; j < n; j++, jn++)
+ {
+ if (s[jn] == t[im])
+ {
+ d[(i * n) + j] = d[((i - 1) * n) + (j - 1)];
+ }
+ else
+ {
+ d[(i * n) + j] = min(d[(i - 1) * n + j] + 1, /* A deletion. */
+ min(d[i * n + (j - 1)] + 1, /* An insertion. */
+ d[(i - 1) * n + (j - 1)] + 1)); /* A substitution. */
+ }
+ }
+ }
+ r = d[n * m - 1];
+ free(d);
+ return r;
+}
+
+#ifdef RASM_THREAD
+/*
+ threads used for crunching
+*/
+void _internal_ExecuteThreads(struct s_assenv *ae,struct s_rasm_thread *rasm_thread, void *(*fct)(void *))
+{
+ #undef FUNC
+ #define FUNC "_internal_ExecuteThreads"
+
+ pthread_attr_t attr;
+ void *status;
+ int rc;
+ /* launch threads */
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
+ pthread_attr_setstacksize(&attr,65536);
+
+ if ((rc=pthread_create(&image_threads[i].thread,&attr,fct,(void *)rasm_thread))) {
+ rasm_printf(ae,"FATAL ERROR - Cannot create thread!\n");
+ exit(INTERNAL_ERROR);
+ }
+}
+void _internal_WaitForThreads(struct s_assenv *ae,struct s_rasm_thread *rasm_thread)
+{
+ #undef FUNC
+ #define FUNC "_internal_WaitForThreads"
+ int rc;
+
+ if ((rc=pthread_join(rasm_thread->thread,&status))) {
+ rasm_printf(ae,"FATAL ERROR - Cannot wait for thread\n");
+ exit(INTERNAL_ERROR);
+ }
+}
+void PushCrunchedFile(struct s_assenv *ae, unsigned char *datain, int datalen, int lz)
+{
+ #undef FUNC
+ #define FUNC "PushCrunchedFile"
+
+ struct s_rasm_thread *rasm_thread;
+
+ rasm_thread=MemMalloc(sizeof(struct s_rasm_thread));
+ memset(rasm_thread,0,sizeof(struct s_rasm_thread));
+ rasm_thread->datain=datain;
+ rasm_thread->datalen=datalen;
+ rasm_thread->lz=lz;
+ _internal_ExecuteThreads(ae,rasm_thread, void *(*fct)(void *));
+ ObjectArrayAddDynamicValueConcat((void**)&ae->rasm_thread,&ae->irt,&ae->mrt,&rasm_thread,sizeof(struct s_rasm_thread *));
+}
+void PopAllCrunchedFiles(struct s_assenv *ae)
+{
+ #undef FUNC
+ #define FUNC "PopAllCrunchedFiles"
+
+ int i;
+ for (i=0;i<ae->irt;i++) {
+ _internal_WaitForThreads(ae,ae->rasm_thread[i]);
+ }
+}
+#endif
+
+void MaxError(struct s_assenv *ae);
+
+void rasm_printf(struct s_assenv *ae, ...) {
+ #undef FUNC
+ #define FUNC "(internal) rasm_printf"
+
+ char *format;
+ va_list argptr;
+
+ if (!ae->flux && !ae->dependencies) {
+ va_start(argptr,ae);
+ format=va_arg(argptr,char *);
+ vfprintf(stdout,format,argptr);
+ va_end(argptr);
+ fprintf(stdout,KNORMAL);
+ }
+}
+/***
+ build the string of current line for error messages
+*/
+char *rasm_getline(struct s_assenv *ae, int offset) {
+ #undef FUNC
+ #define FUNC "rasm_getline"
+
+ static char myline[40]={0};
+ int idx=0,icopy,first=1;
+
+ while (!ae->wl[ae->idx+offset].t && idx<32) {
+ for (icopy=0;idx<32 && ae->wl[ae->idx+offset].w[icopy];icopy++) {
+ myline[idx++]=ae->wl[ae->idx+offset].w[icopy];
+ }
+ if (!first) myline[idx++]=','; else first=0;
+ offset++;
+ }
+ if (idx>=32) {
+ strcpy(myline+29,"...");
+ } else {
+ myline[idx++]=0;
+ }
+
+ return myline;
+}
+
+char *SimplifyPath(char *filename) {
+ #undef FUNC
+ #define FUNC "SimplifyPath"
+
+ return filename;
+#if 0
+ char *pos,*repos;
+ int i,len;
+
+ char *rpath;
+
+ rpath=realpath(filename,NULL);
+ if (!rpath) {
+ printf("rpath error!\n");
+ switch (errno) {
+ case EACCES:printf("read permission failure\n");break;
+ case EINVAL:printf("wrong argument\n");break;
+ case EIO:printf("I/O error\n");break;
+ case ELOOP:printf("too many symbolic links\n");break;
+ case ENAMETOOLONG:printf("names too long\n");break;
+ case ENOENT:printf("names does not exists\n");break;
+ case ENOMEM:printf("out of memory\n");break;
+ case ENOTDIR:printf("a component of the path is not a directory\n");break;
+ default:printf("unknown error\n");break;
+ }
+ exit(1);
+ }
+ if (strlen(rpath)<strlen(filename)) {
+ strcpy(filename,rpath);
+ }
+ free(rpath);
+ return filename;
+
+#ifdef OS_WIN
+ while ((pos=strstr(filename,"\\..\\"))!=NULL) {
+ repos=pos-1;
+ /* sequence found, looking back for '\' */
+ while (repos>=filename) {
+ if (*repos=='\\') {
+ break;
+ }
+ repos--;
+ }
+ repos++;
+ if (repos>=filename && repos!=pos) {
+ len=strlen(pos)-4+1;
+ pos+=4;
+ for (i=0;i<len;i++) {
+ *repos=*pos;
+ repos++;
+ pos++;
+ }
+ }
+ if (strncmp(filename,".\\..\\",5)==0) {
+ repos=filename;
+ pos=repos+2;
+ for (;*repos;pos++,repos++) {
+ *repos=*pos;
+ }
+ *repos=0;
+ }
+ }
+#else
+printf("*************\nfilename=[%s]\n",filename);
+ while ((pos=strstr(filename,"/../"))!=NULL) {
+ repos=pos-1;
+ while (repos>=filename) {
+ if (*repos=='/') {
+ break;
+ }
+ repos--;
+ }
+ repos++;
+ if (repos>=filename && repos!=pos) {
+ len=strlen(pos)-4+1;
+ pos+=4;
+ for (i=0;i<len;i++) {
+ *repos=*pos;
+ repos++;
+ pos++;
+ }
+ }
+printf("filename=[%s]\n",filename);
+ if (strncmp(filename,"./../",5)==0) {
+ repos=filename;
+ pos=repos+2;
+ for (;*repos;pos++,repos++) {
+ *repos=*pos;
+ }
+ *repos=0;
+ }
+printf("filename=[%s]\n",filename);
+ }
+#endif
+ return NULL;
+#endif
+
+}
+
+char *GetPath(char *filename) {
+ #undef FUNC
+ #define FUNC "GetPath"
+
+ static char curpath[PATH_MAX];
+ int zelen,idx;
+
+ zelen=strlen(filename);
+
+#ifdef OS_WIN
+ #define CURRENT_DIR ".\\"
+
+ TxtReplace(filename,"/","\\",1);
+ idx=zelen-1;
+ while (idx>=0 && filename[idx]!='\\') idx--;
+ if (idx<0) {
+ /* pas de chemin */
+ strcpy(curpath,".\\");
+ } else {
+ /* chemin trouve */
+ strcpy(curpath,filename);
+ curpath[idx+1]=0;
+ }
+#else
+#ifdef __MORPHOS__
+ #define CURRENT_DIR ""
+#else
+ #define CURRENT_DIR "./"
+#endif
+ idx=zelen-1;
+ while (idx>=0 && filename[idx]!='/') idx--;
+ if (idx<0) {
+ /* pas de chemin */
+ strcpy(curpath,CURRENT_DIR);
+ } else {
+ /* chemin trouve */
+ strcpy(curpath,filename);
+ curpath[idx+1]=0;
+ }
+#endif
+
+ return curpath;
+}
+char *MergePath(struct s_assenv *ae,char *dadfilename, char *filename) {
+ #undef FUNC
+ #define FUNC "MergePath"
+
+ static char curpath[PATH_MAX];
+ int zelen;
+
+#ifdef OS_WIN
+ TxtReplace(filename,"/","\\",1);
+
+ if (filename[0] && filename[1]==':' && filename[2]=='\\') {
+ /* chemin absolu */
+ strcpy(curpath,filename);
+ } else if (filename[0] && filename[1]==':') {
+ rasm_printf(ae,KERROR"unsupported path style [%s]\n",filename);
+ exit(-111);
+ } else {
+ if (filename[0]=='.' && filename[1]=='\\') {
+ strcpy(curpath,GetPath(dadfilename));
+ strcat(curpath,filename+2);
+ } else {
+ strcpy(curpath,GetPath(dadfilename));
+ strcat(curpath,filename);
+ }
+ }
+#else
+ if (filename[0]=='/') {
+ /* chemin absolu */
+ strcpy(curpath,filename);
+ } else if (filename[0]=='.' && filename[1]=='/') {
+ strcpy(curpath,GetPath(dadfilename));
+ strcat(curpath,filename+2);
+ } else {
+ strcpy(curpath,GetPath(dadfilename));
+ strcat(curpath,filename);
+ }
+#endif
+
+ return curpath;
+}
+
+
+void InitAutomate(char *autotab, const unsigned char *def)
+{
+ #undef FUNC
+ #define FUNC "InitAutomate"
+
+ int i;
+
+ memset(autotab,0,256);
+ for (i=0;def[i];i++) {
+ autotab[(int)def[i]]=1;
+ }
+}
+void StateMachineResizeBuffer(char **ABuf, int idx, int *ASize) {
+ #undef FUNC
+ #define FUNC "StateMachineResizeBuffer"
+
+ if (idx>=*ASize) {
+ if (*ASize<16384) {
+ *ASize=(*ASize)*2;
+ } else {
+ *ASize=(*ASize)+16384;
+ }
+ *ABuf=MemRealloc(*ABuf,(*ASize)+2);
+ }
+}
+
+int GetCRC(char *label)
+{
+ #undef FUNC
+ #define FUNC "GetCRC"
+ int crc=0x12345678;
+ int i=0;
+
+ while (label[i]!=0) {
+ crc=(crc<<9)^(crc+label[i++]);
+ }
+ return crc;
+}
+
+int IsRegister(char *zeexpression)
+{
+ #undef FUNC
+ #define FUNC "IsRegister"
+
+ switch (GetCRC(zeexpression)) {
+ case CRC_F:if (strcmp(zeexpression,"F")==0) return 1; else return 0;
+ case CRC_I:if (strcmp(zeexpression,"I")==0) return 1; else return 0;
+ case CRC_R:if (strcmp(zeexpression,"R")==0) return 1; else return 0;
+ case CRC_A:if (strcmp(zeexpression,"A")==0) return 1; else return 0;
+ case CRC_B:if (strcmp(zeexpression,"B")==0) return 1; else return 0;
+ case CRC_C:if (strcmp(zeexpression,"C")==0) return 1; else return 0;
+ case CRC_D:if (strcmp(zeexpression,"D")==0) return 1; else return 0;
+ case CRC_E:if (strcmp(zeexpression,"E")==0) return 1; else return 0;
+ case CRC_H:if (strcmp(zeexpression,"H")==0) return 1; else return 0;
+ case CRC_L:if (strcmp(zeexpression,"L")==0) return 1; else return 0;
+ case CRC_BC:if (strcmp(zeexpression,"BC")==0) return 1; else return 0;
+ case CRC_DE:if (strcmp(zeexpression,"DE")==0) return 1; else return 0;
+ case CRC_HL:if (strcmp(zeexpression,"HL")==0) return 1; else return 0;
+ case CRC_IX:if (strcmp(zeexpression,"IX")==0) return 1; else return 0;
+ case CRC_IY:if (strcmp(zeexpression,"IY")==0) return 1; else return 0;
+ case CRC_SP:if (strcmp(zeexpression,"SP")==0) return 1; else return 0;
+ case CRC_AF:if (strcmp(zeexpression,"AF")==0) return 1; else return 0;
+ case CRC_XH:if (strcmp(zeexpression,"XH")==0) return 1; else return 0;
+ case CRC_XL:if (strcmp(zeexpression,"XL")==0) return 1; else return 0;
+ case CRC_YH:if (strcmp(zeexpression,"YH")==0) return 1; else return 0;
+ case CRC_YL:if (strcmp(zeexpression,"YL")==0) return 1; else return 0;
+ case CRC_HX:if (strcmp(zeexpression,"HX")==0) return 1; else return 0;
+ case CRC_LX:if (strcmp(zeexpression,"LX")==0) return 1; else return 0;
+ case CRC_HY:if (strcmp(zeexpression,"HY")==0) return 1; else return 0;
+ case CRC_LY:if (strcmp(zeexpression,"LY")==0) return 1; else return 0;
+ case CRC_IXL:if (strcmp(zeexpression,"IXL")==0) return 1; else return 0;
+ case CRC_IXH:if (strcmp(zeexpression,"IXH")==0) return 1; else return 0;
+ case CRC_IYL:if (strcmp(zeexpression,"IYL")==0) return 1; else return 0;
+ case CRC_IYH:if (strcmp(zeexpression,"IYH")==0) return 1; else return 0;
+ default:break;
+ }
+ return 0;
+}
+
+int StringIsMem(char *w)
+{
+ #undef FUNC
+ #define FUNC "StringIsMem"
+
+ int p=1,idx=1;
+
+ if (w[0]=='(') {
+ while (w[idx]) {
+ switch (w[idx]) {
+ case '\\':if (w[idx+1]) idx++;
+ break;
+ case '\'':if (w[idx+1] && w[idx+1]!='\\') idx++;
+ break;
+ case '(':p++;break;
+ case ')':p--;
+ if (!p && w[idx+1]) return 0;
+ break;
+ default:break;
+ }
+ idx++;
+ }
+ if (w[idx-1]!=')') return 0;
+ } else {
+ return 0;
+ }
+ return 1;
+
+}
+int StringIsQuote(char *w)
+{
+ #undef FUNC
+ #define FUNC "StringIsQuote"
+
+ int i,tquote,lens;
+
+ if (w[0]=='\'' || w[0]=='"') {
+ tquote=w[0];
+ lens=strlen(w);
+
+ /* est-ce bien une chaine et uniquement une chaine? */
+ i=1;
+ while (w[i] && w[i]!=tquote) {
+ if (w[i]=='\\') i++;
+ i++;
+ }
+ if (i==lens-1) {
+ return tquote;
+ }
+ }
+ return 0;
+}
+char *StringLooksLikeDicoRecurse(struct s_crcdico_tree *lt, int *score, char *str)
+{
+ #undef FUNC
+ #define FUNC "StringLooksLikeDicoRecurse"
+
+ char *retstr=NULL,*tmpstr;
+ int i,curs;
+
+ for (i=0;i<256;i++) {
+ if (lt->radix[i]) {
+ tmpstr=StringLooksLikeDicoRecurse(lt->radix[i],score,str);
+ if (tmpstr!=NULL) retstr=tmpstr;
+ }
+ }
+ if (lt->mdico) {
+ for (i=0;i<lt->ndico;i++) {
+ if (strlen(lt->dico[i].name)>4) {
+ curs=_internal_LevenshteinDistance(str,lt->dico[i].name);
+ if (curs<*score) {
+ *score=curs;
+ retstr=lt->dico[i].name;
+ }
+ }
+ }
+ }
+ return retstr;
+}
+char *StringLooksLikeDico(struct s_assenv *ae, int *score, char *str)
+{
+ #undef FUNC
+ #define FUNC "StringLooksLikeDico"
+
+ char *retstr=NULL,*tmpstr;
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (ae->dicotree.radix[i]) {
+ tmpstr=StringLooksLikeDicoRecurse(ae->dicotree.radix[i],score,str);
+ if (tmpstr!=NULL) retstr=tmpstr;
+ }
+ }
+ return retstr;
+}
+char *StringLooksLikeMacro(struct s_assenv *ae, char *str, int *retscore)
+{
+ #undef FUNC
+ #define FUNC "StringLooksLikeMacro"
+
+ char *ret=NULL;
+ int i,curs,score=3;
+ /* search in macros */
+ for (i=0;i<ae->imacro;i++) {
+ curs=_internal_LevenshteinDistance(ae->macro[i].mnemo,str);
+ if (curs<score) {
+ score=curs;
+ ret=ae->macro[i].mnemo;
+ }
+ }
+ if (retscore) *retscore=score;
+ return ret;
+}
+
+char *StringLooksLike(struct s_assenv *ae, char *str)
+{
+ #undef FUNC
+ #define FUNC "StringLooksLike"
+
+ char *ret=NULL,*tmpret;
+ int i,curs,score=4;
+
+ /* search in variables */
+ ret=StringLooksLikeDico(ae,&score,str);
+
+ /* search in labels */
+ for (i=0;i<ae->il;i++) {
+ if (!ae->label[i].name && strlen(ae->wl[ae->label[i].iw].w)>4) {
+ curs=_internal_LevenshteinDistance(ae->wl[ae->label[i].iw].w,str);
+ if (curs<score) {
+ score=curs;
+ ret=ae->wl[ae->label[i].iw].w;
+ }
+ }
+ }
+
+ /* search in alias */
+ for (i=0;i<ae->ialias;i++) {
+ if (strlen(ae->alias[i].alias)>4) {
+ curs=_internal_LevenshteinDistance(ae->alias[i].alias,str);
+ if (curs<score) {
+ score=curs;
+ ret=ae->alias[i].alias;
+ }
+ }
+ }
+
+ tmpret=StringLooksLikeMacro(ae,str,&curs);
+ if (curs<score) {
+ score=curs;
+ ret=tmpret;
+ }
+ return ret;
+}
+
+int RoundComputeExpression(struct s_assenv *ae,char *expr, int ptr, int didx, int expression_expected);
+int RoundComputeExpressionCore(struct s_assenv *ae,char *zeexpression,int ptr,int didx);
+double ComputeExpressionCore(struct s_assenv *ae,char *original_zeexpression,int ptr, int didx);
+char *GetExpFile(struct s_assenv *ae,int didx);
+void __STOP(struct s_assenv *ae);
+
+
+void MakeError(struct s_assenv *ae, char *filename, int line, char *format, ...)
+{
+ #undef FUNC
+ #define FUNC "MakeError"
+
+ va_list argptr;
+
+ MaxError(ae);
+ if (ae->flux) {
+ /* in embedded Rasm all errors are stored in a debug struct */
+ struct s_debug_error curerror;
+ char toosmalltotakeitall[2]={0};
+ int myalloc;
+ char *errstr;
+
+ va_start(argptr,format);
+ myalloc=vsnprintf(toosmalltotakeitall,1,format,argptr);
+ va_end(argptr);
+
+ #if defined(_MSC_VER) && _MSC_VER < 1900
+ /* visual studio before 2015 does not fully support C99 */
+ if (myalloc<1 && strlen(format)) {
+ va_start(argptr,format);
+ myalloc=_vscprintf(format,argptr);
+ va_end(argptr);
+ }
+ #endif
+ if (myalloc<1) {
+ /* do not crash */
+ return;
+ }
+
+ va_start(argptr,format);
+ errstr=MemMalloc(myalloc+1);
+ vsnprintf(errstr,myalloc,format,argptr);
+ curerror.msg=errstr;
+ curerror.lenmsg=myalloc;
+ curerror.line=line;
+ if (filename) curerror.filename=TxtStrDupLen(filename,&curerror.lenfilename); else curerror.filename=TxtStrDupLen("<internal>",&curerror.lenfilename);
+ ObjectArrayAddDynamicValueConcat((void **)&ae->debug.error,&ae->debug.nberror,&ae->debug.maxerror,&curerror,sizeof(struct s_debug_error));
+ va_end(argptr);
+ } else {
+ fprintf(stdout,KERROR);
+ va_start(argptr,format);
+ if (filename && line) {
+ printf("[%s:%d] ",filename,line);
+ } else if (filename) {
+ printf("[%s] ",filename);
+ }
+ vfprintf(stdout,format,argptr);
+ va_end(argptr);
+ fprintf(stdout,KNORMAL);
+ }
+}
+
+
+/* convert v double value to Amstrad REAL */
+unsigned char *__internal_MakeAmsdosREAL(struct s_assenv *ae, double v, int iexpression)
+{
+ #undef FUNC
+ #define FUNC "__internal_MakeAmsdosREAL"
+
+ static unsigned char rc[5];
+
+ double tmpval;
+ int j,ib,ibb,exp=0;
+ unsigned int deci;
+ int fracmax=0;
+ double frac;
+ int mesbits[32];
+ int ibit=0;
+ unsigned int mask;
+
+ memset(rc,0,sizeof(rc));
+
+ deci=fabs(floor(v));
+ frac=fabs(v)-deci;
+
+ if (deci) {
+ mask=0x80000000;
+ while (!(deci & mask)) mask=mask/2;
+ while (mask) {
+ mesbits[ibit]=!!(deci & mask);
+#if TRACE_MAKEAMSDOSREAL
+printf("%d",mesbits[ibit]);
+#endif
+ ibit++;
+ mask=mask/2;
+ }
+#if TRACE_MAKEAMSDOSREAL
+printf("\nexposant positif: %d\n",ibit);
+#endif
+ exp=ibit;
+#if TRACE_MAKEAMSDOSREAL
+printf(".");
+#endif
+ while (ibit<32 && frac!=0) {
+ frac=frac*2;
+ if (frac>=1.0) {
+ mesbits[ibit++]=1;
+#if TRACE_MAKEAMSDOSREAL
+printf("1");
+#endif
+ frac-=1.0;
+ } else {
+ mesbits[ibit++]=0;
+#if TRACE_MAKEAMSDOSREAL
+printf("0");
+#endif
+ }
+ fracmax++;
+ }
+ } else {
+#if TRACE_MAKEAMSDOSREAL
+printf("\nexposant negatif a definir:\n");
+printf("x.");
+#endif
+
+ /* handling zero */
+ if (frac==0.0) {
+ exp=0;
+ ibit=0;
+ } else {
+ /* looking for first significant bit */
+ while (1) {
+ frac=frac*2;
+ if (frac>=1.0) {
+ mesbits[ibit++]=1;
+#if TRACE_MAKEAMSDOSREAL
+printf("1");
+#endif
+ frac-=1.0;
+ break; /* first significant bit found, now looking for limit */
+ } else {
+#if TRACE_MAKEAMSDOSREAL
+printf("o");
+#endif
+ }
+ fracmax++;
+ exp--;
+ }
+ while (ibit<32 && frac!=0) {
+ frac=frac*2;
+ if (frac>=1.0) {
+ mesbits[ibit++]=1;
+#if TRACE_MAKEAMSDOSREAL
+printf("1");
+#endif
+ frac-=1.0;
+ } else {
+ mesbits[ibit++]=0;
+#if TRACE_MAKEAMSDOSREAL
+printf("0");
+#endif
+ }
+ fracmax++;
+ }
+ }
+ }
+
+#if TRACE_MAKEAMSDOSREAL
+printf("\n%d bits utilises en mantisse\n",ibit);
+#endif
+ /* pack bits */
+ ib=3;ibb=0x80;
+ for (j=0;j<ibit;j++) {
+ if (mesbits[j]) rc[ib]|=ibb;
+ ibb/=2;
+ if (ibb==0) {
+ ibb=0x80;
+ ib--;
+ }
+ }
+ /* exponent */
+ exp+=128;
+ if (exp<0 || exp>255) {
+ if (iexpression) MakeError(ae,GetExpFile(ae,iexpression),ae->wl[ae->expression[iexpression].iw].l,"Exponent overflow\n");
+ else MakeError(ae,GetExpFile(ae,0),ae->wl[ae->idx].l,"Exponent overflow\n");
+ exp=128;
+ }
+ rc[4]=exp;
+
+ /* REAL sign */
+ if (v>=0) {
+ rc[3]&=0x7F;
+ } else {
+ rc[3]|=0x80;
+ }
+
+#if TRACE_MAKEAMSDOSREAL
+ for (j=0;j<5;j++) printf("%02X ",rc[j]);
+ printf("\n");
+#endif
+
+ return rc;
+}
+
+
+
+
+struct s_label *SearchLabel(struct s_assenv *ae, char *label, int crc);
+char *GetExpFile(struct s_assenv *ae,int didx){
+ #undef FUNC
+ #define FUNC "GetExpFile"
+
+ if (ae->label_filename) {
+ return ae->label_filename;
+ }
+ if (didx<0) {
+ return ae->filename[ae->wl[-didx].ifile];
+ } else if (!didx) {
+ return ae->filename[ae->wl[ae->idx].ifile];
+ } else if (ae->expression && didx<ae->ie) {
+ return ae->filename[ae->wl[ae->expression[didx].iw].ifile];
+ } else {
+ //return ae->filename[ae->wl[ae->idx].ifile];
+ return 0;
+ }
+}
+
+int GetExpLine(struct s_assenv *ae,int didx){
+ #undef FUNC
+ #define FUNC "GetExpLine"
+
+ if (ae->label_line) return ae->label_line;
+
+ if (didx<0) {
+ return ae->wl[-didx].l;
+ } else if (!didx) {
+ return ae->wl[ae->idx].l;
+ } else if (didx<ae->ie) {
+ return ae->wl[ae->expression[didx].iw].l;
+ } else return 0;
+}
+
+char *GetCurrentFile(struct s_assenv *ae)
+{
+ return GetExpFile(ae,0);
+}
+
+
+/*******************************************************************************************
+ M E M O R Y C L E A N U P
+*******************************************************************************************/
+void FreeLabelTree(struct s_assenv *ae);
+void FreeDicoTree(struct s_assenv *ae);
+void FreeUsedTree(struct s_assenv *ae);
+void ExpressionFastTranslate(struct s_assenv *ae, char **ptr_expr, int fullreplace);
+char *TradExpression(char *zexp);
+
+
+void _internal_RasmFreeInfoStruct(struct s_rasm_info *debug)
+{
+ #undef FUNC
+ #define FUNC "RasmFreeInfoStruct"
+
+ int i;
+ if (debug->maxerror) {
+ for (i=0;i<debug->nberror;i++) {
+ MemFree(debug->error[i].filename);
+ MemFree(debug->error[i].msg);
+ }
+ MemFree(debug->error);
+ }
+ if (debug->maxsymbol) {
+ for (i=0;i<debug->nbsymbol;i++) {
+ MemFree(debug->symbol[i].name);
+ }
+ MemFree(debug->symbol);
+ }
+}
+
+void RasmFreeInfoStruct(struct s_rasm_info *debug)
+{
+ _internal_RasmFreeInfoStruct(debug);
+ MemFree(debug);
+}
+
+void FreeAssenv(struct s_assenv *ae)
+{
+ #undef FUNC
+ #define FUNC "FreeAssenv"
+ int i,j;
+
+#ifndef RDD
+ /* let the system free the memory in command line except when debug/dev */
+ if (!ae->flux) return;
+#endif
+ /*** debug info ***/
+ if (!ae->retdebug) {
+ _internal_RasmFreeInfoStruct(&ae->debug);
+ } else {
+ /* symbols */
+ struct s_debug_symbol debug_symbol={0};
+
+ for (i=0;i<ae->il;i++) {
+ /* on exporte tout */
+ if (!ae->label[i].name) {
+ /* les labels entiers */
+ debug_symbol.name=TxtStrDup(ae->wl[ae->label[i].iw].w);
+ debug_symbol.v=ae->label[i].ptr;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->debug.symbol,&ae->debug.nbsymbol,&ae->debug.maxsymbol,&debug_symbol,sizeof(struct s_debug_symbol));
+ } else {
+ /* les labels locaux et générés */
+ debug_symbol.name=TxtStrDup(ae->label[i].name);
+ debug_symbol.v=ae->label[i].ptr;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->debug.symbol,&ae->debug.nbsymbol,&ae->debug.maxsymbol,&debug_symbol,sizeof(struct s_debug_symbol));
+ }
+ }
+ for (i=0;i<ae->ialias;i++) {
+ if (strcmp(ae->alias[i].alias,"IX") && strcmp(ae->alias[i].alias,"IY")) {
+ debug_symbol.name=TxtStrDup(ae->alias[i].alias);
+ debug_symbol.v=RoundComputeExpression(ae,ae->alias[i].translation,0,0,0);
+ ObjectArrayAddDynamicValueConcat((void**)&ae->debug.symbol,&ae->debug.nbsymbol,&ae->debug.maxsymbol,&debug_symbol,sizeof(struct s_debug_symbol));
+ }
+ }
+
+ /* export struct */
+ *ae->retdebug=MemMalloc(sizeof(struct s_rasm_info));
+ memcpy(*ae->retdebug,&ae->debug,sizeof(struct s_rasm_info));
+ }
+ /*** end debug ***/
+
+ for (i=0;i<ae->nbbank;i++) {
+ MemFree(ae->mem[i]);
+ }
+ MemFree(ae->mem);
+
+ /* expression core buffer free */
+ ComputeExpressionCore(NULL,NULL,0,0);
+ ExpressionFastTranslate(NULL,NULL,0);
+ /* free labels, expression, orgzone, repeat, ... */
+ if (ae->mo) MemFree(ae->orgzone);
+ if (ae->me) {
+ for (i=0;i<ae->ie;i++) if (ae->expression[i].reference) MemFree(ae->expression[i].reference);
+ MemFree(ae->expression);
+ }
+ if (ae->mh) {
+ for (i=0;i<ae->ih;i++) {
+ MemFree(ae->hexbin[i].data);
+ MemFree(ae->hexbin[i].filename);
+ }
+ MemFree(ae->hexbin);
+ }
+ for (i=0;i<ae->il;i++) {
+ if (ae->label[i].name && ae->label[i].iw==-1) MemFree(ae->label[i].name);
+ }
+ /* structures */
+ for (i=0;i<ae->irasmstructalias;i++) {
+ MemFree(ae->rasmstructalias[i].name);
+ }
+ if (ae->mrasmstructalias) MemFree(ae->rasmstructalias);
+
+ for (i=0;i<ae->irasmstruct;i++) {
+ for (j=0;j<ae->rasmstruct[i].irasmstructfield;j++) {
+ MemFree(ae->rasmstruct[i].rasmstructfield[j].fullname);
+ MemFree(ae->rasmstruct[i].rasmstructfield[j].name);
+ }
+ if (ae->rasmstruct[i].mrasmstructfield) MemFree(ae->rasmstruct[i].rasmstructfield);
+ MemFree(ae->rasmstruct[i].name);
+ }
+ if (ae->mrasmstruct) MemFree(ae->rasmstruct);
+
+ /* other */
+ if (ae->maxbreakpoint) MemFree(ae->breakpoint);
+ if (ae->ml) MemFree(ae->label);
+ if (ae->mr) MemFree(ae->repeat);
+ if (ae->mi) MemFree(ae->ifthen);
+ if (ae->msw) MemFree(ae->switchcase);
+ if (ae->mw) MemFree(ae->whilewend);
+ if (ae->modulen || ae->module) {
+ MemFree(ae->module);
+ }
+ /* deprecated
+ for (i=0;i<ae->idic;i++) {
+ MemFree(ae->dico[i].name);
+ }
+ if (ae->mdic) MemFree(ae->dico);
+ */
+ if (ae->mlz) MemFree(ae->lzsection);
+
+ for (i=0;i<ae->ifile;i++) {
+ MemFree(ae->filename[i]);
+ }
+ MemFree(ae->filename);
+
+ for (i=0;i<ae->imacro;i++) {
+ if (ae->macro[i].maxword) MemFree(ae->macro[i].wc);
+ for (j=0;j<ae->macro[i].nbparam;j++) MemFree(ae->macro[i].param[j]);
+ if (ae->macro[i].nbparam) MemFree(ae->macro[i].param);
+ }
+
+
+ if (ae->mmacro) MemFree(ae->macro);
+
+ for (i=0;i<ae->ialias;i++) {
+ MemFree(ae->alias[i].alias);
+ MemFree(ae->alias[i].translation);
+ }
+ if (ae->malias) MemFree(ae->alias);
+
+ for (i=0;ae->wl[i].t!=2;i++) {
+ MemFree(ae->wl[i].w);
+ }
+ MemFree(ae->wl);
+
+ if (ae->ctx1.varbuffer) {
+ MemFree(ae->ctx1.varbuffer);
+ }
+ if (ae->ctx1.maxtokenstack) {
+ MemFree(ae->ctx1.tokenstack);
+ }
+ if (ae->ctx1.maxoperatorstack) {
+ MemFree(ae->ctx1.operatorstack);
+ }
+ if (ae->ctx2.varbuffer) {
+ MemFree(ae->ctx2.varbuffer);
+ }
+ if (ae->ctx2.maxtokenstack) {
+ MemFree(ae->ctx2.tokenstack);
+ }
+ if (ae->ctx2.maxoperatorstack) {
+ MemFree(ae->ctx2.operatorstack);
+ }
+
+ for (i=0;i<ae->iticker;i++) {
+ MemFree(ae->ticker[i].varname);
+ }
+ if (ae->mticker) MemFree(ae->ticker);
+
+ MemFree(ae->outputfilename);
+ FreeLabelTree(ae);
+ FreeDicoTree(ae);
+ FreeUsedTree(ae);
+ if (ae->mmacropos) MemFree(ae->macropos);
+ TradExpression(NULL);
+ MemFree(ae);
+}
+
+
+
+void MaxError(struct s_assenv *ae)
+{
+ #undef FUNC
+ #define FUNC "MaxError"
+
+ char **source_lines=NULL;
+ int idx,crc,zeline;
+ char c;
+
+ /* extended error is useful with generated code we do not want to edit */
+ if (ae->extended_error && ae->wl) {
+ /* super dupper slow but anyway, there is an error... */
+ if (ae->wl[ae->idx].l) {
+ source_lines=FileReadLinesRAW(GetCurrentFile(ae));
+ zeline=0;
+ while (zeline<ae->wl[ae->idx].l-1 && source_lines[zeline]) zeline++;
+ if (zeline==ae->wl[ae->idx].l-1 && source_lines[zeline]) {
+ rasm_printf(ae,KAYGREEN"-> %s",source_lines[zeline]);
+ } else {
+ rasm_printf(ae,KERROR"cannot read line %d of file [%s]\n",ae->wl[ae->idx].l,GetCurrentFile(ae));
+ }
+ FreeArrayDynamicValue(&source_lines);
+ }
+ }
+
+ ae->nberr++;
+ if (ae->nberr==ae->maxerr) {
+ rasm_printf(ae,KERROR"Too many errors!\n");
+ FreeAssenv(ae);
+ exit(ae->nberr);
+ }
+}
+
+void (*___output)(struct s_assenv *ae, unsigned char v);
+
+void ___internal_output_disabled(struct s_assenv *ae,unsigned char v)
+{
+ #undef FUNC
+ #define FUNC "fake ___output"
+}
+void ___internal_output(struct s_assenv *ae,unsigned char v)
+{
+ #undef FUNC
+ #define FUNC "___output"
+
+ if (ae->outputadr<ae->maxptr) {
+ ae->mem[ae->activebank][ae->outputadr++]=v;
+ ae->codeadr++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"output exceed limit %d\n",ae->maxptr);
+ ae->stop=1;
+ ___output=___internal_output_disabled;
+ }
+}
+void ___internal_output_nocode(struct s_assenv *ae,unsigned char v)
+{
+ #undef FUNC
+ #define FUNC "___output (nocode)"
+
+ if (ae->outputadr<ae->maxptr) {
+ /* struct definition always in NOCODE */
+ if (ae->getstruct) {
+ int irs,irsf;
+ irs=ae->irasmstruct-1;
+ irsf=ae->rasmstruct[irs].irasmstructfield-1;
+
+ /* ajouter les data du flux au champ de la structure */
+ ObjectArrayAddDynamicValueConcat((void**)&ae->rasmstruct[irs].rasmstructfield[irsf].data,
+ &ae->rasmstruct[irs].rasmstructfield[irsf].idata,
+ &ae->rasmstruct[irs].rasmstructfield[irsf].mdata,
+ &v,sizeof(unsigned char));
+ }
+
+ ae->outputadr++;
+ ae->codeadr++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"output exceed limit %d\n",ae->maxptr);
+ ae->stop=1;
+ ___output=___internal_output_disabled;
+ }
+}
+
+
+void ___output_set_limit(struct s_assenv *ae,int zelimit)
+{
+ #undef FUNC
+ #define FUNC "___output_set_limit"
+
+ int limit=65535;
+
+ if (zelimit<=limit) {
+ /* apply limit */
+ limit=zelimit;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"limit exceed hardware limitation!");
+ ae->stop=1;
+ }
+ if (ae->outputadr>=0 && ae->outputadr>limit) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"limit too high for current output!");
+ ae->stop=1;
+ }
+ ae->maxptr=limit;
+}
+
+unsigned char *MakeAMSDOSHeader(int run, int minmem, int maxmem, char *amsdos_name) {
+ #undef FUNC
+ #define FUNC "MakeAMSDOSHeader"
+
+ static unsigned char AmsdosHeader[128];
+ int checksum,i=0;
+ /*** cpcwiki
+ Byte 00: User number
+ Byte 01 to 08: filename
+ Byte 09 bis 11: Extension
+ Byte 18: type-byte
+ Byte 21 and 22: loading address
+ Byte 24 and 25: file length
+ Byte 26 and 27: execution address for machine code programs
+ Byte 64 and 65: (file length)
+ Byte 67 and 68: checksum for byte 00 to byte 66
+ To calculate the checksum, just add byte 00 to byte 66 to each other.
+ */
+ memset(AmsdosHeader,0,sizeof(AmsdosHeader));
+ AmsdosHeader[0]=0;
+ memcpy(AmsdosHeader+1,amsdos_name,11);
+
+ AmsdosHeader[18]=2; /* 0 basic 1 basic protege 2 binaire */
+ AmsdosHeader[19]=(maxmem-minmem)&0xFF;
+ AmsdosHeader[20]=(maxmem-minmem)>>8;
+ AmsdosHeader[21]=minmem&0xFF;
+ AmsdosHeader[22]=minmem>>8;
+ AmsdosHeader[24]=AmsdosHeader[19];
+ AmsdosHeader[25]=AmsdosHeader[20];
+ AmsdosHeader[26]=run&0xFF;
+ AmsdosHeader[27]=run>>8;
+ AmsdosHeader[64]=AmsdosHeader[19];
+ AmsdosHeader[65]=AmsdosHeader[20];
+ AmsdosHeader[66]=0;
+
+ for (i=checksum=0;i<=66;i++) {
+ checksum+=AmsdosHeader[i];
+ }
+ AmsdosHeader[67]=checksum&0xFF;
+ AmsdosHeader[68]=checksum>>8;
+
+ /* garbage / shadow values from sector buffer? */
+ memcpy(AmsdosHeader+0x47,amsdos_name,8);
+ AmsdosHeader[0x4F]=0x24;
+ AmsdosHeader[0x50]=0x24;
+ AmsdosHeader[0x51]=0x24;
+ AmsdosHeader[0x52]=0xFF;
+ AmsdosHeader[0x54]=0xFF;
+ AmsdosHeader[0x57]=0x02;
+ AmsdosHeader[0x5A]=AmsdosHeader[21];
+ AmsdosHeader[0x5B]=AmsdosHeader[22];
+ AmsdosHeader[0x5D]=AmsdosHeader[24];
+ AmsdosHeader[0x5E]=AmsdosHeader[25];
+
+ sprintf((char *)AmsdosHeader+0x47+17," generated by %s ",RASM_SNAP_VERSION);
+
+ return AmsdosHeader;
+}
+
+unsigned char *MakeHobetaHeader(int minmem, int maxmem, char *trdos_name) {
+ #undef FUNC
+ #define FUNC "MakeHobetaHeader"
+
+ static unsigned char HobetaHeader[17];
+ int i,checksum=0;
+ /*** http://rk.nvg.ntnu.no/sinclair/faq/fileform.html#HOBETA
+ 0x00 FileName 0x08 TR-DOS file name
+ 0x08 FileType 0x01 TR-DOS file type
+ 0x09 StartAdr 0x02 start address of file
+ 0x0A FlLength 0x02 length of file (in bytes) -> /!\ wrong offset!!!
+ 0x0C FileSize 0x02 size of file (in sectors)
+ 0x0E HdrCRC16 0x02 Control checksum of the 15 byte
+ header (not sector data!)
+ */
+ memset(HobetaHeader,0,sizeof(HobetaHeader));
+
+ strncpy(HobetaHeader,trdos_name,8);
+ HobetaHeader[8]='C';
+ HobetaHeader[0x9]=(maxmem-minmem)&0xFF;
+ HobetaHeader[0xA]=(maxmem-minmem)>>8;
+
+ HobetaHeader[0xB]=(maxmem-minmem)&0xFF;
+ HobetaHeader[0xC]=(maxmem-minmem)>>8;
+
+ HobetaHeader[0xD]=((maxmem-minmem)+255)>>8;
+ HobetaHeader[0xE]=0;
+
+ for (i=0;i<0xF;i++) checksum+=HobetaHeader[i]*257+i;
+
+ HobetaHeader[0xF]=checksum&0xFF;
+ HobetaHeader[0x10]=(checksum>>8)&0xFF;
+
+ return HobetaHeader;
+}
+
+
+int cmpAmsdosentry(const void * a, const void * b)
+{
+ return memcmp(a,b,32);
+}
+
+int cmpmacros(const void * a, const void * b)
+{
+ struct s_macro *sa,*sb;
+ sa=(struct s_macro *)a;
+ sb=(struct s_macro *)b;
+ if (sa->crc<sb->crc) return -1; else return 1;
+}
+int SearchAlias(struct s_assenv *ae, int crc, char *zemot)
+{
+ int dw,dm,du,i;
+//printf("SearchAlias [%s] ",zemot);
+ /* inutile de tourner autour du pot pour un si petit nombre */
+ if (ae->ialias<5) {
+ for (i=0;i<ae->ialias;i++) {
+ if (ae->alias[i].crc==crc && strcmp(ae->alias[i].alias,zemot)==0) {
+ ae->alias[i].used=1;
+//printf("found\n");
+ return i;
+ }
+ }
+//printf("not found\n");
+ return -1;
+ }
+
+ dw=0;
+ du=ae->ialias-1;
+ while (dw<=du) {
+ dm=(dw+du)/2;
+ if (ae->alias[dm].crc==crc) {
+ /* chercher le premier de la liste */
+ while (dm>0 && ae->alias[dm-1].crc==crc) dm--;
+ /* controle sur le texte entier */
+ while (ae->alias[dm].crc==crc && strcmp(ae->alias[dm].alias,zemot)) dm++;
+ if (ae->alias[dm].crc==crc && strcmp(ae->alias[dm].alias,zemot)==0) {
+ ae->alias[dm].used=1;
+//printf("found\n");
+ return dm;
+ } else return -1;
+ } else if (ae->alias[dm].crc>crc) {
+ du=dm-1;
+ } else if (ae->alias[dm].crc<crc) {
+ dw=dm+1;
+ }
+ }
+//printf("not found\n");
+ return -1;
+}
+int SearchMacro(struct s_assenv *ae, int crc, char *zemot)
+{
+ int dw,dm,du,i;
+
+ /* inutile de tourner autour du pot pour un si petit nombre */
+ if (ae->imacro<5) {
+ for (i=0;i<ae->imacro;i++) {
+ if (ae->macro[i].crc==crc && strcmp(ae->macro[i].mnemo,zemot)==0) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ dw=0;
+ du=ae->imacro-1;
+ while (dw<=du) {
+ dm=(dw+du)/2;
+ if (ae->macro[dm].crc==crc) {
+ /* chercher le premier de la liste */
+ while (dm>0 && ae->macro[dm-1].crc==crc) dm--;
+ /* controle sur le texte entier */
+ while (ae->macro[dm].crc==crc && strcmp(ae->macro[dm].mnemo,zemot)) dm++;
+ if (ae->macro[dm].crc==crc && strcmp(ae->macro[dm].mnemo,zemot)==0) return dm; else return -1;
+ } else if (ae->macro[dm].crc>crc) {
+ du=dm-1;
+ } else if (ae->macro[dm].crc<crc) {
+ dw=dm+1;
+ }
+ }
+ return -1;
+}
+
+void CheckAndSortAliases(struct s_assenv *ae)
+{
+ #undef FUNC
+ #define FUNC "CheckAndSortAliases"
+
+ struct s_alias tmpalias;
+ int i,dw,dm,du,crc;
+ for (i=0;i<ae->ialias-1;i++) {
+ /* is there previous aliases in the new alias? */
+ if (strstr(ae->alias[ae->ialias-1].translation,ae->alias[i].alias)) {
+ /* there is a match, apply alias translation */
+ ExpressionFastTranslate(ae,&ae->alias[ae->ialias-1].translation,2);
+ /* need to compute again len */
+ ae->alias[ae->ialias-1].len=strlen(ae->alias[ae->ialias-1].translation);
+ break;
+ }
+ }
+
+ /* cas particuliers pour insertion en début ou fin de liste */
+ if (ae->ialias-1) {
+ if (ae->alias[ae->ialias-1].crc>ae->alias[ae->ialias-2].crc) {
+ /* pas de tri il est déjà au bon endroit */
+ } else if (ae->alias[ae->ialias-1].crc<ae->alias[0].crc) {
+ /* insertion tout en bas de liste */
+ tmpalias=ae->alias[ae->ialias-1];
+ MemMove(&ae->alias[1],&ae->alias[0],sizeof(struct s_alias)*(ae->ialias-1));
+ ae->alias[0]=tmpalias;
+ } else {
+ /* on cherche ou inserer */
+ crc=ae->alias[ae->ialias-1].crc;
+ dw=0;
+ du=ae->ialias-1;
+ while (dw<=du) {
+ dm=(dw+du)/2;
+ if (ae->alias[dm].crc==crc) {
+ break;
+ } else if (ae->alias[dm].crc>crc) {
+ du=dm-1;
+ } else if (ae->alias[dm].crc<crc) {
+ dw=dm+1;
+ }
+ }
+ /* ajustement */
+ if (ae->alias[dm].crc<crc) dm++;
+ /* insertion */
+ tmpalias=ae->alias[ae->ialias-1];
+ MemMove(&ae->alias[dm+1],&ae->alias[dm],sizeof(struct s_alias)*(ae->ialias-1-dm));
+ ae->alias[dm]=tmpalias;
+ }
+ } else {
+ /* one alias need no sort */
+ }
+}
+
+void InsertDicoToTree(struct s_assenv *ae, struct s_expr_dico *dico)
+{
+ #undef FUNC
+ #define FUNC "InsertDicoToTree"
+
+ struct s_crcdico_tree *curdicotree;
+ int radix,dek=32;
+
+ curdicotree=&ae->dicotree;
+ while (dek) {
+ dek=dek-8;
+ radix=(dico->crc>>dek)&0xFF;
+ if (curdicotree->radix[radix]) {
+ curdicotree=curdicotree->radix[radix];
+ } else {
+ curdicotree->radix[radix]=MemMalloc(sizeof(struct s_crcdico_tree));
+ curdicotree=curdicotree->radix[radix];
+ memset(curdicotree,0,sizeof(struct s_crcdico_tree));
+ }
+ }
+ ObjectArrayAddDynamicValueConcat((void**)&curdicotree->dico,&curdicotree->ndico,&curdicotree->mdico,dico,sizeof(struct s_expr_dico));
+}
+
+unsigned char *SnapshotDicoInsert(char *symbol_name, int ptr, int *retidx)
+{
+ static unsigned char *subchunk=NULL;
+ static int subchunksize=0;
+ static int idx=0;
+ int symbol_len;
+
+ if (retidx) {
+ if (symbol_name && strcmp(symbol_name,"FREE")==0) {
+ subchunksize=0;
+ idx=0;
+ MemFree(subchunk);
+ subchunk=NULL;
+ }
+ *retidx=idx;
+ return subchunk;
+ }
+
+ if (idx+65536>subchunksize) {
+ subchunksize=subchunksize+65536;
+ subchunk=MemRealloc(subchunk,subchunksize);
+ }
+
+ symbol_len=strlen(symbol_name);
+ if (symbol_len>255) symbol_len=255;
+ subchunk[idx++]=symbol_len;
+ memcpy(subchunk+idx,symbol_name,symbol_len);
+ idx+=symbol_len;
+ memset(subchunk+idx,0,6);
+ idx+=6;
+ subchunk[idx++]=(ptr&0xFF00)/256;
+ subchunk[idx++]=ptr&0xFF;
+ return NULL;
+}
+
+void SnapshotDicoTreeRecurse(struct s_crcdico_tree *lt)
+{
+ #undef FUNC
+ #define FUNC "SnapshottDicoTreeRecurse"
+
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (lt->radix[i]) {
+ SnapshotDicoTreeRecurse(lt->radix[i]);
+ }
+ }
+ if (lt->mdico) {
+ for (i=0;i<lt->ndico;i++) {
+ if (strcmp(lt->dico[i].name,"IX") && strcmp(lt->dico[i].name,"IY") && strcmp(lt->dico[i].name,"PI") && strcmp(lt->dico[i].name,"ASSEMBLER_RASM")) {
+ SnapshotDicoInsert(lt->dico[i].name,(int)floor(lt->dico[i].v+0.5),NULL);
+ }
+ }
+ }
+}
+unsigned char *SnapshotDicoTree(struct s_assenv *ae, int *retidx)
+{
+ #undef FUNC
+ #define FUNC "SnapshotDicoTree"
+
+ unsigned char *sc;
+ int idx;
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (ae->dicotree.radix[i]) {
+ SnapshotDicoTreeRecurse(ae->dicotree.radix[i]);
+ }
+ }
+
+ sc=SnapshotDicoInsert(NULL,0,&idx);
+ *retidx=idx;
+ return sc;
+}
+
+void WarnLabelTreeRecurse(struct s_assenv *ae, struct s_crclabel_tree *lt)
+{
+ #undef FUNC
+ #define FUNC "WarnLabelTreeRecurse"
+
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (lt->radix[i]) {
+ WarnLabelTreeRecurse(ae,lt->radix[i]);
+ }
+ }
+ for (i=0;i<lt->nlabel;i++) {
+ if (!lt->label[i].used) {
+ if (!lt->label[i].name) {
+ rasm_printf(ae,KWARNING"[%s:%d] Warning: label %s declared but not used\n",ae->filename[lt->label[i].fileidx],lt->label[i].fileline,ae->wl[lt->label[i].iw].w);
+ } else {
+ rasm_printf(ae,KWARNING"[%s:%d] Warning: label %s declared but not used\n",ae->filename[lt->label[i].fileidx],lt->label[i].fileline,lt->label[i].name);
+ }
+ }
+ }
+}
+void WarnLabelTree(struct s_assenv *ae)
+{
+ #undef FUNC
+ #define FUNC "WarnLabelTree"
+
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (ae->labeltree.radix[i]) {
+ WarnLabelTreeRecurse(ae,ae->labeltree.radix[i]);
+ }
+ }
+}
+void WarnDicoTreeRecurse(struct s_assenv *ae, struct s_crcdico_tree *lt)
+{
+ #undef FUNC
+ #define FUNC "WarnDicoTreeRecurse"
+
+ char symbol_line[1024];
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (lt->radix[i]) {
+ WarnDicoTreeRecurse(ae,lt->radix[i]);
+ }
+ }
+ for (i=0;i<lt->ndico;i++) {
+ if (strcmp(lt->dico[i].name,"IX") && strcmp(lt->dico[i].name,"IY") && strcmp(lt->dico[i].name,"PI") && strcmp(lt->dico[i].name,"ASSEMBLER_RASM") && lt->dico[i].autorise_export) {
+ rasm_printf(ae,KWARNING"[%s:%d] Warning: variable %s declared but not used\n",ae->filename[ae->wl[lt->dico[i].iw].ifile],ae->wl[lt->dico[i].iw].l,lt->dico[i].name);
+ }
+ }
+}
+void WarnDicoTree(struct s_assenv *ae)
+{
+ #undef FUNC
+ #define FUNC "ExportDicoTree"
+
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (ae->dicotree.radix[i]) {
+ WarnDicoTreeRecurse(ae,ae->dicotree.radix[i]);
+ }
+ }
+}
+void ExportDicoTreeRecurse(struct s_crcdico_tree *lt, char *zefile, char *zeformat)
+{
+ #undef FUNC
+ #define FUNC "ExportDicoTreeRecurse"
+
+ char symbol_line[1024];
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (lt->radix[i]) {
+ ExportDicoTreeRecurse(lt->radix[i],zefile,zeformat);
+ }
+ }
+ if (lt->mdico) {
+ for (i=0;i<lt->ndico;i++) {
+ if (strcmp(lt->dico[i].name,"IX") && strcmp(lt->dico[i].name,"IY") && strcmp(lt->dico[i].name,"PI") && strcmp(lt->dico[i].name,"ASSEMBLER_RASM") && lt->dico[i].autorise_export) {
+ snprintf(symbol_line,sizeof(symbol_line)-1,zeformat,lt->dico[i].name,(int)floor(lt->dico[i].v+0.5));
+ symbol_line[sizeof(symbol_line)-1]=0xD;
+ FileWriteLine(zefile,symbol_line);
+ }
+ }
+ }
+}
+void ExportDicoTree(struct s_assenv *ae, char *zefile, char *zeformat)
+{
+ #undef FUNC
+ #define FUNC "ExportDicoTree"
+
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (ae->dicotree.radix[i]) {
+ ExportDicoTreeRecurse(ae->dicotree.radix[i],zefile,zeformat);
+ }
+ }
+}
+void FreeDicoTreeRecurse(struct s_crcdico_tree *lt)
+{
+ #undef FUNC
+ #define FUNC "FreeDicoTreeRecurse"
+
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (lt->radix[i]) {
+ FreeDicoTreeRecurse(lt->radix[i]);
+ }
+ }
+ if (lt->mdico) {
+ for (i=0;i<lt->ndico;i++) {
+ MemFree(lt->dico[i].name);
+ }
+ MemFree(lt->dico);
+ }
+ MemFree(lt);
+}
+void FreeDicoTree(struct s_assenv *ae)
+{
+ #undef FUNC
+ #define FUNC "FreeDicoTree"
+
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (ae->dicotree.radix[i]) {
+ FreeDicoTreeRecurse(ae->dicotree.radix[i]);
+ }
+ }
+ if (ae->dicotree.mdico) {
+ for (i=0;i<ae->dicotree.ndico;i++) MemFree(ae->dicotree.dico[i].name);
+ MemFree(ae->dicotree.dico);
+ }
+}
+struct s_expr_dico *SearchDico(struct s_assenv *ae, char *dico, int crc)
+{
+ #undef FUNC
+ #define FUNC "SearchDico"
+
+ struct s_crcdico_tree *curdicotree;
+ struct s_expr_dico *retdico=NULL;
+ int i,radix,dek=32;
+
+ curdicotree=&ae->dicotree;
+
+ while (dek) {
+ dek=dek-8;
+ radix=(crc>>dek)&0xFF;
+ if (curdicotree->radix[radix]) {
+ curdicotree=curdicotree->radix[radix];
+ } else {
+ /* radix not found, dico is not in index */
+ return NULL;
+ }
+ }
+ for (i=0;i<curdicotree->ndico;i++) {
+ if (strcmp(curdicotree->dico[i].name,dico)==0) {
+ curdicotree->dico[i].used=1;
+ return &curdicotree->dico[i];
+ }
+ }
+ return NULL;
+}
+int DelDico(struct s_assenv *ae, char *dico, int crc)
+{
+ #undef FUNC
+ #define FUNC "DelDico"
+
+ struct s_crcdico_tree *curdicotree;
+ struct s_expr_dico *retdico=NULL;
+ int i,radix,dek=32;
+
+ curdicotree=&ae->dicotree;
+
+ while (dek) {
+ dek=dek-8;
+ radix=(crc>>dek)&0xFF;
+ if (curdicotree->radix[radix]) {
+ curdicotree=curdicotree->radix[radix];
+ } else {
+ /* radix not found, dico is not in index */
+ return 0;
+ }
+ }
+ for (i=0;i<curdicotree->ndico;i++) {
+ if (strcmp(curdicotree->dico[i].name,dico)==0) {
+ /* must free memory */
+ MemFree(curdicotree->dico[i].name);
+ if (i<curdicotree->ndico-1) {
+ MemMove(&curdicotree->dico[i],&curdicotree->dico[i+1],(curdicotree->ndico-i-1)*sizeof(struct s_expr_dico));
+ }
+ curdicotree->ndico--;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+void InsertUsedToTree(struct s_assenv *ae, char *used, int crc)
+{
+ #undef FUNC
+ #define FUNC "InsertUsedToTree"
+
+ struct s_crcused_tree *curusedtree;
+ int radix,dek=32,i;
+
+ curusedtree=&ae->usedtree;
+ while (dek) {
+ dek=dek-8;
+ radix=(crc>>dek)&0xFF;
+ if (curusedtree->radix[radix]) {
+ curusedtree=curusedtree->radix[radix];
+ } else {
+ curusedtree->radix[radix]=MemMalloc(sizeof(struct s_crcused_tree));
+ curusedtree=curusedtree->radix[radix];
+ memset(curusedtree,0,sizeof(struct s_crcused_tree));
+ }
+ }
+ for (i=0;i<curusedtree->nused;i++) if (strcmp(used,curusedtree->used[i])==0) break;
+ /* no double */
+ if (i==curusedtree->nused) {
+ FieldArrayAddDynamicValueConcat(&curusedtree->used,&curusedtree->nused,&curusedtree->mused,used);
+ }
+}
+
+void FreeUsedTreeRecurse(struct s_crcused_tree *lt)
+{
+ #undef FUNC
+ #define FUNC "FreeUsedTreeRecurse"
+
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (lt->radix[i]) {
+ FreeUsedTreeRecurse(lt->radix[i]);
+ }
+ }
+ if (lt->mused) {
+ for (i=0;i<lt->nused;i++) MemFree(lt->used[i]);
+ MemFree(lt->used);
+ }
+ MemFree(lt);
+}
+void FreeUsedTree(struct s_assenv *ae)
+{
+ #undef FUNC
+ #define FUNC "FreeUsedTree"
+
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (ae->usedtree.radix[i]) {
+ FreeUsedTreeRecurse(ae->usedtree.radix[i]);
+ }
+ }
+}
+int SearchUsed(struct s_assenv *ae, char *used, int crc)
+{
+ #undef FUNC
+ #define FUNC "SearchUsed"
+
+ struct s_crcused_tree *curusedtree;
+ int i,radix,dek=32;
+
+ curusedtree=&ae->usedtree;
+ while (dek) {
+ dek=dek-8;
+ radix=(crc>>dek)&0xFF;
+ if (curusedtree->radix[radix]) {
+ curusedtree=curusedtree->radix[radix];
+ } else {
+ /* radix not found, used is not in index */
+ return 0;
+ }
+ }
+ for (i=0;i<curusedtree->nused;i++) {
+ if (strcmp(curusedtree->used[i],used)==0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+
+void InsertTextToTree(struct s_assenv *ae, char *text, char *replace, int crc)
+{
+ #undef FUNC
+ #define FUNC "InsertTextToTree"
+
+ struct s_crcstring_tree *curstringtree;
+ int radix,dek=32,i;
+
+ curstringtree=&ae->stringtree;
+ while (dek) {
+ dek=dek-8;
+ radix=(crc>>dek)&0xFF;
+ if (curstringtree->radix[radix]) {
+ curstringtree=curstringtree->radix[radix];
+ } else {
+ curstringtree->radix[radix]=MemMalloc(sizeof(struct s_crcused_tree));
+ curstringtree=curstringtree->radix[radix];
+ memset(curstringtree,0,sizeof(struct s_crcused_tree));
+ }
+ }
+ for (i=0;i<curstringtree->ntext;i++) if (strcmp(text,curstringtree->text[i])==0) break;
+ /* no double */
+ if (i==curstringtree->ntext) {
+ text=TxtStrDup(text);
+ replace=TxtStrDup(replace);
+ FieldArrayAddDynamicValueConcat(&curstringtree->text,&curstringtree->ntext,&curstringtree->mtext,text);
+ FieldArrayAddDynamicValueConcat(&curstringtree->replace,&curstringtree->nreplace,&curstringtree->mreplace,replace);
+ }
+}
+
+void FreeTextTreeRecurse(struct s_crcstring_tree *lt)
+{
+ #undef FUNC
+ #define FUNC "FreeTextTreeRecurse"
+
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (lt->radix[i]) {
+ FreeTextTreeRecurse(lt->radix[i]);
+ }
+ }
+ if (lt->mtext) {
+ for (i=0;i<lt->ntext;i++) MemFree(lt->text[i]);
+ MemFree(lt->text);
+ }
+ MemFree(lt);
+}
+void FreeTextTree(struct s_assenv *ae)
+{
+ #undef FUNC
+ #define FUNC "FreeTextTree"
+
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (ae->stringtree.radix[i]) {
+ FreeTextTreeRecurse(ae->stringtree.radix[i]);
+ }
+ }
+ if (ae->stringtree.mtext) MemFree(ae->stringtree.text);
+}
+int SearchText(struct s_assenv *ae, char *text, int crc)
+{
+ #undef FUNC
+ #define FUNC "SearchText"
+
+ struct s_crcstring_tree *curstringtree;
+ int i,radix,dek=32;
+
+ curstringtree=&ae->stringtree;
+ while (dek) {
+ dek=dek-8;
+ radix=(crc>>dek)&0xFF;
+ if (curstringtree->radix[radix]) {
+ curstringtree=curstringtree->radix[radix];
+ } else {
+ /* radix not found, used is not in index */
+ return 0;
+ }
+ }
+ for (i=0;i<curstringtree->ntext;i++) {
+ if (strcmp(curstringtree->text[i],text)==0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+
+
+
+
+
+/*
+struct s_crclabel_tree {
+
+
+
+
+/*
+struct s_crclabel_tree {
+ struct s_crclabel_tree *radix[256];
+ struct s_label *label;
+ int nlabel,mlabel;
+};
+*/
+void FreeLabelTreeRecurse(struct s_crclabel_tree *lt)
+{
+ #undef FUNC
+ #define FUNC "FreeLabelTreeRecurse"
+
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (lt->radix[i]) {
+ FreeLabelTreeRecurse(lt->radix[i]);
+ }
+ }
+ /* label.name already freed elsewhere as this one is a copy */
+ if (lt->mlabel) MemFree(lt->label);
+ MemFree(lt);
+}
+void FreeLabelTree(struct s_assenv *ae)
+{
+ #undef FUNC
+ #define FUNC "FreeLabelTree"
+
+ int i;
+
+ for (i=0;i<256;i++) {
+ if (ae->labeltree.radix[i]) {
+ FreeLabelTreeRecurse(ae->labeltree.radix[i]);
+ }
+ }
+ if (ae->labeltree.mlabel) MemFree(ae->labeltree.label);
+}
+
+struct s_label *SearchLabel(struct s_assenv *ae, char *label, int crc)
+{
+ #undef FUNC
+ #define FUNC "SearchLabel"
+
+ struct s_crclabel_tree *curlabeltree;
+ struct s_label *retlabel=NULL;
+ int i,radix,dek=32;
+//printf("searchLabel [%s]",label);
+ curlabeltree=&ae->labeltree;
+ while (dek) {
+ dek=dek-8;
+ radix=(crc>>dek)&0xFF;
+ if (curlabeltree->radix[radix]) {
+ curlabeltree=curlabeltree->radix[radix];
+ } else {
+ /* radix not found, label is not in index */
+//printf(" not found\n");
+ return NULL;
+ }
+ }
+ for (i=0;i<curlabeltree->nlabel;i++) {
+ if (!curlabeltree->label[i].name && strcmp(ae->wl[curlabeltree->label[i].iw].w,label)==0) {
+ curlabeltree->label[i].used=1;
+//printf(" found (global)\n");
+ return &curlabeltree->label[i];
+ } else if (curlabeltree->label[i].name && strcmp(curlabeltree->label[i].name,label)==0) {
+ curlabeltree->label[i].used=1;
+//printf(" found (local or proximity)\n");
+ return &curlabeltree->label[i];
+ }
+ }
+ return NULL;
+}
+
+char *MakeLocalLabel(struct s_assenv *ae,char *varbuffer, int *retdek)
+{
+ #undef FUNC
+ #define FUNC "MakeLocalLabel"
+
+ char *locallabel;
+ char hexdigit[32];
+ int lenbuf=0,dek,i,im;
+ char *zepoint;
+
+ lenbuf=strlen(varbuffer);
+
+ /* not so local labels */
+ if (varbuffer[0]=='.') {
+ /* create reference */
+ if (ae->lastgloballabel) {
+ locallabel=MemMalloc(strlen(varbuffer)+1+ae->lastgloballabellen);
+ sprintf(locallabel,"%s%s",ae->lastgloballabel,varbuffer);
+ if (retdek) *retdek=0;
+ return locallabel;
+ } else {
+ if (retdek) *retdek=0;
+ return TxtStrDup(varbuffer);
+ }
+ }
+
+ /***************************************************
+ without retdek -> build a local label
+ with retdek -> build the hash string
+ ***************************************************/
+ if (!retdek) {
+ locallabel=MemMalloc(lenbuf+(ae->ir+ae->iw+3)*8+8);
+ zepoint=strchr(varbuffer,'.');
+ if (zepoint) {
+ *zepoint=0;
+ }
+ strcpy(locallabel,varbuffer);
+ } else {
+ locallabel=MemMalloc((ae->ir+ae->iw+3)*8+4);
+ locallabel[0]=0;
+ }
+//printf("locallabel=[%s] (draft)\n",locallabel);
+
+ dek=0;
+ dek+=strappend(locallabel,"R");
+ for (i=0;i<ae->ir;i++) {
+ sprintf(hexdigit,"%04X",ae->repeat[i].cpt);
+ dek+=strappend(locallabel,hexdigit);
+ }
+ if (ae->ir) {
+ sprintf(hexdigit,"%04X",ae->repeat[ae->ir-1].value);
+ dek+=strappend(locallabel+dek,hexdigit);
+ }
+
+ dek+=strappend(locallabel,"W");
+ for (i=0;i<ae->iw;i++) {
+ sprintf(hexdigit,"%04X",ae->whilewend[i].cpt);
+ dek+=strappend(locallabel+dek,hexdigit);
+ }
+ if (ae->iw) {
+ sprintf(hexdigit,"%04X",ae->whilewend[ae->iw-1].value);
+ dek+=strappend(locallabel+dek,hexdigit);
+ }
+ /* where are we? */
+ if (ae->imacropos) {
+ for (im=ae->imacropos-1;im>=0;im--) {
+ if (ae->idx>=ae->macropos[im].start && ae->idx<ae->macropos[im].end) break;
+ }
+ if (im>=0) {
+ /* si on n'est pas dans une macro, on n'indique rien */
+ sprintf(hexdigit,"M%04X",ae->macropos[im].value);
+ dek+=strappend(locallabel+dek,hexdigit);
+ }
+ }
+ if (!retdek) {
+ if (zepoint) {
+ *zepoint='.';
+ strcat(locallabel+dek,zepoint);
+ }
+ } else {
+ *retdek=dek;
+ }
+//printf("locallabel=[%s] (end)\n",locallabel);
+ return locallabel;
+}
+
+char *TradExpression(char *zexp)
+{
+ #undef FUNC
+ #define FUNC "TradExpression"
+
+ static char *last_expression=NULL;
+ char *wstr;
+
+ if (last_expression) {MemFree(last_expression);last_expression=NULL;}
+ if (!zexp) return NULL;
+
+ wstr=TxtStrDup(zexp);
+ wstr=TxtReplace(wstr,"[","<<",0);
+ wstr=TxtReplace(wstr,"]",">>",0);
+ wstr=TxtReplace(wstr,"m","%",0);
+
+ last_expression=wstr;
+ return wstr;
+}
+
+int TrimFloatingPointString(char *fps) {
+ int i=0,pflag,zflag=0;
+
+ while (fps[i]) {
+ if (fps[i]=='.') {
+ pflag=i;
+ zflag=1;
+ } else if (fps[i]!='0') {
+ zflag=0;
+ }
+ i++;
+ }
+ /* truncate floating fract */
+ if (zflag) {
+ fps[pflag]=0;
+ } else {
+ pflag=i;
+ }
+ return pflag;
+}
+
+
+
+/*
+ translate tag or formula between curly brackets
+ used in label declaration
+ used in print directive
+*/
+char *TranslateTag(struct s_assenv *ae, char *varbuffer, int *touched, int enablefast, int tagoption) {
+ /*******************************************************
+ v a r i a b l e s i n s t r i n g s
+ *******************************************************/
+ char *starttag,*endtag,*tagcheck,*expr;
+ int newlen,lenw,taglen,tagidx,tagcount,validx;
+ char curvalstr[256]={0};
+ char *equpos=NULL,*equback;
+
+//printf("TranslateTag [%s]\n",varbuffer);
+
+ if (tagoption & E_TAGOPTION_PRESERVE) {
+ if (ae->iw || ae->ir) {
+ /* inside a loop we must care about variables */
+//printf("TranslateTag [%s] with PRESERVE inside a loop!\n",varbuffer);
+ return varbuffer;
+ }
+ }
+
+ *touched=0;
+ while ((starttag=strchr(varbuffer+1,'{'))!=NULL) {
+ if ((endtag=strchr(starttag,'}'))==NULL) {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"invalid tag in string [%s]\n",varbuffer);
+ return NULL;
+ }
+ /* allow inception */
+ tagcount=1;
+ tagcheck=starttag+1;
+ while (*tagcheck && tagcount) {
+ if (*tagcheck=='}') tagcount--; else if (*tagcheck=='{') tagcount++;
+ tagcheck++;
+ }
+ if (tagcount) {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"invalid brackets combination in string [%s]\n",varbuffer);
+ return NULL;
+ } else {
+ endtag=tagcheck-1;
+ }
+ *touched=1;
+ taglen=endtag-starttag+1;
+ tagidx=starttag-varbuffer;
+ lenw=strlen(varbuffer); // before the EOF write
+ *endtag=0;
+ /*** c o m p u t e e x p r e s s i o n ***/
+ expr=TxtStrDup(starttag+1);
+ if (tagoption & E_TAGOPTION_REMOVESPACE) expr=TxtReplace(expr," ","",0);
+ if (enablefast) ExpressionFastTranslate(ae,&expr,0);
+ validx=(int)RoundComputeExpressionCore(ae,expr,ae->codeadr,0);
+ if (validx<0) {
+ strcpy(curvalstr,"");
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"indexed tag must NOT be a negative value [%s]\n",varbuffer);
+ MemFree(expr);
+ return NULL;
+ } else {
+ #ifdef OS_WIN
+ snprintf(curvalstr,sizeof(curvalstr)-1,"%d",validx);
+ newlen=strlen(curvalstr);
+ #else
+ newlen=snprintf(curvalstr,sizeof(curvalstr)-1,"%d",validx);
+ #endif
+ }
+ MemFree(expr);
+ if (newlen>taglen) {
+ /* realloc */
+ varbuffer=MemRealloc(varbuffer,lenw+newlen-taglen+1);
+ }
+ if (newlen!=taglen ) {
+ MemMove(varbuffer+tagidx+newlen,varbuffer+tagidx+taglen,lenw-taglen-tagidx+1);
+ }
+ strncpy(varbuffer+tagidx,curvalstr,newlen); /* copy without zero terminator */
+ }
+
+ return varbuffer;
+}
+
+double ComputeExpressionCore(struct s_assenv *ae,char *original_zeexpression,int ptr, int didx)
+{
+ #undef FUNC
+ #define FUNC "ComputeExpressionCore"
+
+ /* static execution buffers */
+ static double *accu=NULL;
+ static int maccu=0;
+ static struct s_compute_element *computestack=NULL;
+ static int maxcomputestack=0;
+ int i,j,paccu=0;
+ int nbtokenstack=0;
+ int nbcomputestack=0;
+ int nboperatorstack=0;
+
+ struct s_compute_element stackelement;
+ int o2,okclose,itoken;
+
+ int idx=0,crc,icheck,is_binary,ivar=0;
+ char asciivalue[11];
+ unsigned char c;
+ int accu_err=0;
+ /* backup alias replace */
+ char *zeexpression,*expr;
+ int original=1;
+ int ialias,startvar;
+ int newlen,lenw;
+ /* dictionnary */
+ struct s_expr_dico *curdic;
+ struct s_label *curlabel;
+ char *localname;
+ int minusptr,imkey,bank,page,idxmacro;
+ double curval;
+ /* negative value */
+ int allow_minus_as_sign=0;
+ /* extended replace in labels */
+ int curly=0,curlyflag=0;
+ char *Automate;
+ double dummint;
+
+ /* memory cleanup */
+ if (!ae) {
+ if (maccu) MemFree(accu);
+ accu=NULL;maccu=0;
+ if (maxcomputestack) MemFree(computestack);
+ computestack=NULL;maxcomputestack=0;
+#if 0
+ if (maxivar) MemFree(varbuffer);
+ if (maxtokenstack) MemFree(tokenstack);
+ if (maxoperatorstack) MemFree(operatorstack);
+ maxtokenstack=maxoperatorstack=0;
+ maxivar=1;
+ varbuffer=NULL;
+ tokenstack=NULL;
+ operatorstack=NULL;
+#endif
+ return 0.0;
+ }
+
+ /* be sure to have at least some bytes allocated */
+ StateMachineResizeBuffer(&ae->computectx->varbuffer,128,&ae->computectx->maxivar);
+
+
+#if TRACE_COMPUTE_EXPRESSION
+ printf("expression=[%s]\n",zeexpression);
+#endif
+ zeexpression=original_zeexpression;
+ if (!zeexpression[0]) {
+ return 0;
+ }
+ /* double hack if the first value is negative */
+ if (zeexpression[0]=='-') {
+ if (ae->AutomateExpressionValidCharFirst[(int)zeexpression[1]&0xFF]) {
+ allow_minus_as_sign=1;
+ } else {
+ memset(&stackelement,0,sizeof(stackelement));
+ ObjectArrayAddDynamicValueConcat((void **)&ae->computectx->tokenstack,&nbtokenstack,&ae->computectx->maxtokenstack,&stackelement,sizeof(stackelement));
+ }
+ }
+
+ /* is there ascii char? */
+ while ((c=zeexpression[idx])!=0) {
+ if (c=='\'' || c=='"') {
+ /* echappement */
+ if (zeexpression[idx+1]=='\\') {
+ if (zeexpression[idx+2] && zeexpression[idx+3]==c) {
+ sprintf(asciivalue,"#%03X",zeexpression[idx+2]);
+ memcpy(zeexpression+idx,asciivalue,4);
+ idx+=3;
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Only single escaped char may be quoted [%s]\n",TradExpression(zeexpression));
+ zeexpression[0]=0;
+ return 0;
+ }
+ } else if (zeexpression[idx+1] && zeexpression[idx+2]==c) {
+ sprintf(asciivalue,"#%02X",zeexpression[idx+1]);
+ memcpy(zeexpression+idx,asciivalue,3);
+ idx+=2;
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Only single char may be quoted [%s]\n",TradExpression(zeexpression));
+ zeexpression[0]=0;
+ return 0;
+ }
+ }
+
+ idx++;
+ }
+#if TRACE_COMPUTE_EXPRESSION
+ printf("apres conversion des chars [%s]\n",zeexpression);
+#endif
+ /***********************************************************
+ P A T C H F O R P O S I T I V E V A L U E
+ ***********************************************************/
+ if (zeexpression[0]=='+') idx=1; else idx=0;
+ /***********************************************************
+ C O M P U T E E X P R E S S I O N M A I N L O O P
+ ***********************************************************/
+ while ((c=zeexpression[idx])!=0) {
+ switch (c) {
+ /* parenthesis */
+ case ')':
+ /* next to a closing parenthesis, a minus is an operator */
+ allow_minus_as_sign=0;
+ break;
+ case '(':
+ /* operator detection */
+ case '*':
+ case '/':
+ case '^':
+ case '[':
+ case 'm':
+ case '+':
+ case ']':
+ allow_minus_as_sign=1;
+ break;
+ case '&':
+ allow_minus_as_sign=1;
+ if (c=='&' && zeexpression[idx+1]=='&') {
+ idx++;
+ c='a'; // boolean AND
+ }
+ break;
+ case '|':
+ allow_minus_as_sign=1;
+ if (c=='|' && zeexpression[idx+1]=='|') {
+ idx++;
+ c='o'; // boolean OR
+ }
+ break;
+ /* testing */
+ case '<':
+ allow_minus_as_sign=1;
+ if (zeexpression[idx+1]=='=') {
+ idx++;
+ c='k'; // boolean LOWEREQ
+ } else if (zeexpression[idx+1]=='>') {
+ idx++;
+ c='n'; // boolean NOTEQUAL
+ } else {
+ c='l';
+ }
+ break;
+ case '>':
+ allow_minus_as_sign=1;
+ if (zeexpression[idx+1]=='=') {
+ idx++;
+ c='h'; // boolean GREATEREQ
+ } else {
+ c='g';
+ }
+ break;
+ case '!':
+ allow_minus_as_sign=1;
+ if (zeexpression[idx+1]=='=') {
+ idx++;
+ c='n'; // boolean NOTEQUAL
+ } else {
+ c='b';
+ }
+ break;
+ case '=':
+ allow_minus_as_sign=1;
+ /* expecting == */
+ if (zeexpression[idx+1]=='=') {
+ idx++;
+ c='e'; // boolean EQUAL
+ /* except in maxam mode with a single = */
+ } else if (ae->maxam) {
+ c='e'; // boolean EQUAL
+ /* cannot affect data inside an expression */
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] cannot set variable inside an expression\n",TradExpression(zeexpression));
+ return 0;
+ }
+ break;
+ case '-':
+ if (allow_minus_as_sign) {
+ /* previous char was an opening parenthesis or an operator */
+ ivar=0;
+ ae->computectx->varbuffer[ivar++]='-';
+ StateMachineResizeBuffer(&ae->computectx->varbuffer,ivar,&ae->computectx->maxivar);
+ c=zeexpression[++idx];
+ if (ae->AutomateExpressionValidCharFirst[(int)c&0xFF]) {
+ ae->computectx->varbuffer[ivar++]=c;
+ StateMachineResizeBuffer(&ae->computectx->varbuffer,ivar,&ae->computectx->maxivar);
+ c=zeexpression[++idx];
+ while (ae->AutomateExpressionValidChar[(int)c&0xFF]) {
+ ae->computectx->varbuffer[ivar++]=c;
+ StateMachineResizeBuffer(&ae->computectx->varbuffer,ivar,&ae->computectx->maxivar);
+ c=zeexpression[++idx];
+ }
+ }
+ ae->computectx->varbuffer[ivar]=0;
+ if (ivar<2) {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] invalid minus sign\n",TradExpression(zeexpression));
+ if (!original) {
+ MemFree(zeexpression);
+ }
+ return 0;
+ }
+ break;
+ }
+ allow_minus_as_sign=1;
+ break;
+
+ /* operator OR binary value */
+ case '%':
+ /* % symbol may be a modulo or a binary literal value */
+ is_binary=0;
+ for (icheck=1;zeexpression[idx+icheck];icheck++) {
+ switch (zeexpression[idx+icheck]) {
+ case '1':
+ case '0':/* still binary */
+ is_binary=1;
+ break;
+ case '+':
+ case '-':
+ case '/':
+ case '*':
+ case '|':
+ case 'm':
+ case '%':
+ case '^':
+ case '&':
+ case '(':
+ case ')':
+ case '=':
+ case '<':
+ case '>':
+ case '!':
+ case '[':
+ case ']':
+ if (is_binary) is_binary=2; else is_binary=-1;
+ break;
+ default:
+ is_binary=-1;
+ }
+ if (is_binary==2) {
+ break;
+ }
+ if (is_binary==-1) {
+ is_binary=0;
+ break;
+ }
+ }
+ if (!is_binary) {
+ allow_minus_as_sign=1;
+ c='m';
+ break;
+ }
+ default:
+ allow_minus_as_sign=0;
+ /* semantic analysis */
+ startvar=idx;
+ ivar=0;
+ /* first char does not allow same chars as next chars */
+ if (ae->AutomateExpressionValidCharFirst[((int)c)&0xFF]) {
+ ae->computectx->varbuffer[ivar++]=c;
+ if (c=='{') {
+ /* not a formula but only a prefix tag */
+ curly++;
+ }
+ StateMachineResizeBuffer(&ae->computectx->varbuffer,ivar,&ae->computectx->maxivar);
+ idx++;
+ c=zeexpression[idx];
+ Automate=ae->AutomateExpressionValidChar;
+ while (Automate[((int)c)&0xFF]) {
+ if (c=='{') {
+ curly++;
+ curlyflag=1;
+ Automate=ae->AutomateExpressionValidCharExtended;
+ } else if (c=='}') {
+ curly--;
+ if (!curly) {
+ Automate=ae->AutomateExpressionValidChar;
+ }
+ }
+ ae->computectx->varbuffer[ivar++]=c;
+ StateMachineResizeBuffer(&ae->computectx->varbuffer,ivar,&ae->computectx->maxivar);
+ idx++;
+ c=zeexpression[idx];
+ }
+ }
+ ae->computectx->varbuffer[ivar]=0;
+ if (!ivar) {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"invalid char (%d=%c) expression [%s]\n",c,c>31?c:' ',TradExpression(zeexpression));
+ if (!original) {
+ MemFree(zeexpression);
+ }
+ return 0;
+ } else if (curly) {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"wrong curly brackets in expression [%s]\n",TradExpression(zeexpression));
+ if (!original) {
+ MemFree(zeexpression);
+ }
+ return 0;
+ }
+ }
+ if (c && !ivar) idx++;
+
+ /************************************
+ S T A C K D I S P A T C H E R
+ ************************************/
+ /* push operator or stack value */
+ if (!ivar) {
+ /************************************
+ O P E R A T O R
+ ************************************/
+ stackelement=ae->AutomateElement[c];
+ if (stackelement.operator>E_COMPUTE_OPERATION_GREATEREQ) {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] has unknown operator %c (%d)\n",TradExpression(zeexpression),c>31?c:'.',c);
+ }
+ /* stackelement.value isn't used */
+ } else {
+ /************************************
+ V A L U E
+ ************************************/
+#if TRACE_COMPUTE_EXPRESSION
+ printf("value [%s]\n",ae->computectx->varbuffer);
+#endif
+ if (ae->computectx->varbuffer[0]=='-') minusptr=1; else minusptr=0;
+ /* constantes ou variables/labels */
+ switch (ae->computectx->varbuffer[minusptr]) {
+ case '0':
+ /* 0x hexa value hack */
+ if (ae->computectx->varbuffer[minusptr+1]=='X' && ae->AutomateHexa[ae->computectx->varbuffer[minusptr+2]]) {
+ for (icheck=minusptr+3;ae->computectx->varbuffer[icheck];icheck++) {
+ if (ae->AutomateHexa[ae->computectx->varbuffer[icheck]]) continue;
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid hex number\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ break;
+ }
+ curval=strtol(ae->computectx->varbuffer+minusptr+2,NULL,16);
+ break;
+ } else
+ /* 0b binary value hack */
+ if (ae->computectx->varbuffer[minusptr+1]=='B' && (ae->computectx->varbuffer[minusptr+2]>='0' && ae->computectx->varbuffer[minusptr+2]<='1')) {
+ for (icheck=minusptr+3;ae->computectx->varbuffer[icheck];icheck++) {
+ if (ae->computectx->varbuffer[icheck]>='0' && ae->computectx->varbuffer[icheck]<='1') continue;
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid binary number\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ break;
+ }
+ curval=strtol(ae->computectx->varbuffer+minusptr+2,NULL,2);
+ break;
+ }
+ /* 0o octal value hack */
+ if (ae->computectx->varbuffer[minusptr+1]=='O' && (ae->computectx->varbuffer[minusptr+2]>='0' && ae->computectx->varbuffer[minusptr+2]<='5')) {
+ for (icheck=minusptr+3;ae->computectx->varbuffer[icheck];icheck++) {
+ if (ae->computectx->varbuffer[icheck]>='0' && ae->computectx->varbuffer[icheck]<='5') continue;
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid octal number\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ break;
+ }
+ curval=strtol(ae->computectx->varbuffer+minusptr+2,NULL,2);
+ break;
+ }
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ /* check number */
+ for (icheck=minusptr;ae->computectx->varbuffer[icheck];icheck++) {
+ if (ae->AutomateDigit[ae->computectx->varbuffer[icheck]]) continue;
+ /* Intel hexa & binary style */
+ switch (ae->computectx->varbuffer[strlen(ae->computectx->varbuffer)-1]) {
+ case 'H':
+ for (icheck=minusptr;ae->computectx->varbuffer[icheck+1];icheck++) {
+ if (ae->AutomateHexa[ae->computectx->varbuffer[icheck]]) continue;
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid hex number\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ }
+ curval=strtol(ae->computectx->varbuffer+minusptr,NULL,16);
+ break;
+ case 'B':
+ for (icheck=minusptr;ae->computectx->varbuffer[icheck+1];icheck++) {
+ if (ae->computectx->varbuffer[icheck]=='0' || ae->computectx->varbuffer[icheck]=='1') continue;
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid binary number\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ }
+ curval=strtol(ae->computectx->varbuffer+minusptr,NULL,2);
+ break;
+ default:
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid number\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ }
+ icheck=0;
+ break;
+ }
+ if (!ae->computectx->varbuffer[icheck]) curval=atof(ae->computectx->varbuffer+minusptr);
+ break;
+ case '%':
+ /* check number */
+ if (!ae->computectx->varbuffer[minusptr+1]) {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is an empty binary number\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ }
+ for (icheck=minusptr+1;ae->computectx->varbuffer[icheck];icheck++) {
+ if (ae->computectx->varbuffer[icheck]=='0' || ae->computectx->varbuffer[icheck]=='1') continue;
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid binary number\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ break;
+ }
+ curval=strtol(ae->computectx->varbuffer+minusptr+1,NULL,2);
+ break;
+ case '#':
+ /* check number */
+ if (!ae->computectx->varbuffer[minusptr+1]) {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is an empty hex number\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ }
+ for (icheck=minusptr+1;ae->computectx->varbuffer[icheck];icheck++) {
+ if (ae->AutomateHexa[ae->computectx->varbuffer[icheck]]) continue;
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid hex number\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ break;
+ }
+ curval=strtol(ae->computectx->varbuffer+minusptr+1,NULL,16);
+ break;
+ default:
+ if (1 || !curlyflag) {
+ /* $ hex value hack */
+ if (ae->computectx->varbuffer[minusptr+0]=='$' && ae->AutomateHexa[ae->computectx->varbuffer[minusptr+1]]) {
+ for (icheck=minusptr+2;ae->computectx->varbuffer[icheck];icheck++) {
+ if (ae->AutomateHexa[ae->computectx->varbuffer[icheck]]) continue;
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid hex number\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ break;
+ }
+ curval=strtol(ae->computectx->varbuffer+minusptr+1,NULL,16);
+ break;
+ }
+ /* @ octal value hack */
+ if (ae->computectx->varbuffer[minusptr+0]=='@' && ((ae->computectx->varbuffer[minusptr+1]>='0' && ae->computectx->varbuffer[minusptr+1]<='7'))) {
+ for (icheck=minusptr+2;ae->computectx->varbuffer[icheck];icheck++) {
+ if (ae->computectx->varbuffer[icheck]>='0' && ae->computectx->varbuffer[icheck]<='7') continue;
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid octal number\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ break;
+ }
+ curval=strtol(ae->computectx->varbuffer+minusptr+1,NULL,8);
+ break;
+ }
+ /* Intel hexa value hack */
+ if (ae->AutomateHexa[ae->computectx->varbuffer[minusptr+0]]) {
+ if (ae->computectx->varbuffer[strlen(ae->computectx->varbuffer)-1]=='H') {
+ for (icheck=minusptr;ae->computectx->varbuffer[icheck+1];icheck++) {
+ if (!ae->AutomateHexa[ae->computectx->varbuffer[icheck]]) break;
+ }
+ if (!ae->computectx->varbuffer[icheck+1]) {
+ curval=strtol(ae->computectx->varbuffer+minusptr,NULL,16);
+ break;
+ }
+ }
+ }
+ }
+
+
+ if (curlyflag) {
+ char *minivarbuffer;
+ int touched;
+
+ /* besoin d'un sous-contexte */
+ minivarbuffer=TxtStrDup(ae->computectx->varbuffer+minusptr);
+ ae->computectx=&ae->ctx2;
+#if TRACE_COMPUTE_EXPRESSION
+ printf("curly [%s]\n",minivarbuffer);
+#endif
+ minivarbuffer=TranslateTag(ae,minivarbuffer, &touched,0,E_TAGOPTION_NONE);
+#if TRACE_COMPUTE_EXPRESSION
+ printf("après curly [%s]\n",minivarbuffer);
+#endif
+ ae->computectx=&ae->ctx1;
+ if (!touched) {
+ strcpy(ae->computectx->varbuffer+minusptr,minivarbuffer);
+ } else {
+ StateMachineResizeBuffer(&ae->computectx->varbuffer,strlen(minivarbuffer)+2,&ae->computectx->maxivar);
+ strcpy(ae->computectx->varbuffer+minusptr,minivarbuffer);
+ }
+ MemFree(minivarbuffer);
+ curlyflag=0;
+ }
+
+ crc=GetCRC(ae->computectx->varbuffer+minusptr);
+ /***************************************************
+ L O O K I N G F O R A F U N C T I O N
+ ***************************************************/
+ for (imkey=0;math_keyword[imkey].mnemo[0];imkey++) {
+ if (crc==math_keyword[imkey].crc && strcmp(ae->computectx->varbuffer+minusptr,math_keyword[imkey].mnemo)==0) {
+ if (c=='(') {
+ /* push function as operator! */
+ stackelement.operator=math_keyword[imkey].operation;
+ //stackelement.priority=0;
+ /************************************************
+ C R E A T E E X T R A T O K E N
+ ************************************************/
+ ObjectArrayAddDynamicValueConcat((void **)&ae->computectx->tokenstack,&nbtokenstack,&ae->computectx->maxtokenstack,&stackelement,sizeof(stackelement));
+ stackelement.operator=E_COMPUTE_OPERATION_OPEN;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->computectx->tokenstack,&nbtokenstack,&ae->computectx->maxtokenstack,&stackelement,sizeof(stackelement));
+ allow_minus_as_sign=1;
+ idx++;
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is a reserved keyword!\n",TradExpression(zeexpression),math_keyword[imkey].mnemo);
+ curval=0;
+ idx++;
+ }
+ ivar=0;
+ break;
+ }
+ }
+ if (math_keyword[imkey].mnemo[0]) continue;
+
+ if (ae->computectx->varbuffer[minusptr+0]=='$' && ae->computectx->varbuffer[minusptr+1]==0) {
+ curval=ptr;
+ } else {
+#if TRACE_COMPUTE_EXPRESSION
+ printf("search dico [%s]\n",ae->computectx->varbuffer+minusptr);
+#endif
+ curdic=SearchDico(ae,ae->computectx->varbuffer+minusptr,crc);
+ if (curdic) {
+#if TRACE_COMPUTE_EXPRESSION
+ printf("trouvé valeur=%.2lf\n",curdic->v);
+#endif
+ curval=curdic->v;
+ break;
+ } else {
+ /* getbank hack */
+ if (ae->computectx->varbuffer[minusptr]!='{') {
+ bank=0;
+ page=0;
+ } else if (strncmp(ae->computectx->varbuffer+minusptr,"{BANK}",6)==0) {
+ bank=6;
+ page=0;
+ /* obligé de recalculer le CRC */
+ crc=GetCRC(ae->computectx->varbuffer+minusptr+bank);
+ } else if (strncmp(ae->computectx->varbuffer+minusptr,"{PAGE}",6)==0) {
+ bank=6;
+ page=1;
+ /* obligé de recalculer le CRC */
+ crc=GetCRC(ae->computectx->varbuffer+minusptr+bank);
+ } else if (strncmp(ae->computectx->varbuffer+minusptr,"{PAGESET}",9)==0) {
+ bank=9;
+ page=2;
+ /* obligé de recalculer le CRC */
+ crc=GetCRC(ae->computectx->varbuffer+minusptr+bank);
+ } else if (strncmp(ae->computectx->varbuffer+minusptr,"{SIZEOF}",8)==0) {
+ bank=8;
+ page=3;
+ /* obligé de recalculer le CRC */
+ crc=GetCRC(ae->computectx->varbuffer+minusptr+bank);
+ /* search in structures prototypes and subfields */
+ for (i=0;i<ae->irasmstruct;i++) {
+ if (ae->rasmstruct[i].crc==crc && strcmp(ae->rasmstruct[i].name,ae->computectx->varbuffer+minusptr+bank)==0) {
+ curval=ae->rasmstruct[i].size;
+ break;
+ }
+
+ for (j=0;j<ae->rasmstruct[i].irasmstructfield;j++) {
+ if (ae->rasmstruct[i].rasmstructfield[j].crc==crc && strcmp(ae->rasmstruct[i].rasmstructfield[j].fullname,ae->computectx->varbuffer+minusptr+bank)==0) {
+ curval=ae->rasmstruct[i].rasmstructfield[j].size;
+ i=ae->irasmstruct+1;
+ break;
+ }
+ }
+ }
+
+ if (i==ae->irasmstruct) {
+ /* search in structures aliases */
+ for (i=0;i<ae->irasmstructalias;i++) {
+ if (ae->rasmstructalias[i].crc==crc && strcmp(ae->rasmstructalias[i].name,ae->computectx->varbuffer+minusptr+bank)==0) {
+ curval=ae->rasmstructalias[i].size+ae->rasmstructalias[i].ptr;
+ break;
+ }
+ }
+ if (i==ae->irasmstructalias) {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"cannot SIZEOF unknown structure [%s]!\n",ae->computectx->varbuffer+minusptr+bank);
+ curval=0;
+ }
+ }
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is an unknown prefix!\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ }
+ /* limited label translation while processing crunched blocks
+ ae->curlz == current crunched block processed
+ expression->crunch_block=0 -> oui
+ expression->crunch_block=1 -> oui si même block
+ expression->crunch_block=2 -> non car sera relogée
+ */
+ if (page!=3) {
+
+#if TRACE_COMPUTE_EXPRESSION
+printf("search label [%s]\n",ae->computectx->varbuffer+minusptr+bank);
+#endif
+ curlabel=SearchLabel(ae,ae->computectx->varbuffer+minusptr+bank,crc);
+ if (curlabel) {
+ if (ae->stage<2) {
+ if (curlabel->lz==-1) {
+ if (!bank) {
+ curval=curlabel->ptr;
+ } else {
+#if TRACE_COMPUTE_EXPRESSION
+printf("page=%d | ptr=%X ibank=%d\n",page,curlabel->ptr,curlabel->ibank);
+#endif
+ switch (page) {
+ case 2: /* PAGESET */
+ if (curlabel->ibank<BANK_MAX_NUMBER) {
+ curval=ae->setgate[curlabel->ibank];
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] cannot use PAGESET - label [%s] is in a temporary space!\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ curval=curlabel->ibank;
+ }
+ break;
+ case 1:/* PAGE */
+ if (curlabel->ibank<BANK_MAX_NUMBER) {
+ /* 4M expansion compliant */
+ if (ae->bankset[curlabel->ibank>>2]) {
+ curval=ae->bankgate[(curlabel->ibank&0x1FC)+(curlabel->ptr>>14)];
+ } else {
+ curval=ae->bankgate[curlabel->ibank];
+ }
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] cannot use PAGE - label [%s] is in a temporary space!\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ curval=curlabel->ibank;
+ }
+ break;
+ case 0:
+ curval=curlabel->ibank;
+ break;
+ default:MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INTERNAL ERROR (unknown paging)\n",GetExpFile(ae,didx),GetExpLine(ae,didx));FreeAssenv(ae);exit(-664);
+ }
+ }
+ } else {
+ /* label MUST be in the crunched block */
+ if (curlabel->iorgzone==ae->expression[didx].iorgzone && curlabel->ibank==ae->expression[didx].ibank && curlabel->lz<=ae->expression[didx].lz) {
+ if (!bank) {
+ curval=curlabel->ptr;
+ } else {
+ if (page) {
+ switch (page) {
+ case 2: /* PAGESET */
+ if (curlabel->ibank<BANK_MAX_NUMBER) {
+ /* 4M expansion compliant */
+ curval=ae->setgate[curlabel->ibank];
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] cannot use PAGESET - label [%s] is in a temporary space!\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ curval=curlabel->ibank;
+ }
+ break;
+ case 1: /* PAGE */
+ if (curlabel->ibank<BANK_MAX_NUMBER) {
+ /* 4M expansion compliant */
+ if (ae->bankset[curlabel->ibank>>2]) {
+ curval=ae->bankgate[(curlabel->ibank&0x1FC)+(curlabel->ptr>>14)];
+ } else {
+ curval=ae->bankgate[curlabel->ibank];
+ }
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] cannot use PAGE - label [%s] is in a temporary space!\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ curval=curlabel->ibank;
+ }
+ break;
+ case 0:curval=curlabel->ibank;break;
+ default:MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INTERNAL ERROR (unknown paging)\n",GetExpFile(ae,didx),GetExpLine(ae,didx));FreeAssenv(ae);exit(-664);
+ }
+ }
+ }
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Label [%s](%d) cannot be computed because it is located after the crunched zone %d\n",ae->computectx->varbuffer,curlabel->lz,ae->expression[didx].lz);
+ curval=0;
+ }
+ }
+ } else {
+#if TRACE_COMPUTE_EXPRESSION
+printf("stage 2 | page=%d | ptr=%X ibank=%d\n",page,curlabel->ptr,curlabel->ibank);
+#endif
+ if (bank) {
+ //curval=curlabel->ibank;
+ switch (page) {
+ case 2: /* PAGESET */
+ if (curlabel->ibank<BANK_MAX_NUMBER) {
+ curval=ae->setgate[curlabel->ibank];
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] cannot use PAGESET - label [%s] is in a temporary space!\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ curval=curlabel->ibank;
+ }
+ break;
+ case 1:/* PAGE */
+ if (curlabel->ibank<BANK_MAX_NUMBER) {
+ /* 4M expansion compliant */
+ if (ae->bankset[curlabel->ibank>>2]) {
+ curval=ae->bankgate[(curlabel->ibank&0x1FC)+(curlabel->ptr>>14)];
+ } else {
+ curval=ae->bankgate[curlabel->ibank];
+ }
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] cannot use PAGE - label [%s] is in a temporary space!\n",TradExpression(zeexpression),ae->computectx->varbuffer);
+ curval=curlabel->ibank;
+ }
+ break;
+ case 0:
+ curval=curlabel->ibank;
+ break;
+ default:MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INTERNAL ERROR (unknown paging)\n",GetExpFile(ae,didx),GetExpLine(ae,didx));FreeAssenv(ae);exit(-664);
+ }
+ } else {
+ curval=curlabel->ptr;
+ }
+ }
+ } else {
+ /***********
+ to allow aliases declared after use
+ ***********/
+ if ((ialias=SearchAlias(ae,crc,ae->computectx->varbuffer+minusptr))>=0) {
+ newlen=ae->alias[ialias].len;
+ lenw=strlen(zeexpression);
+ if (newlen>ivar) {
+ /* realloc bigger */
+ if (original) {
+ expr=MemMalloc(lenw+newlen-ivar+1);
+ memcpy(expr,zeexpression,lenw+1);
+ zeexpression=expr;
+ original=0;
+ } else {
+ zeexpression=MemRealloc(zeexpression,lenw+newlen-ivar+1);
+ }
+ }
+ /* startvar? */
+ if (newlen!=ivar) {
+ MemMove(zeexpression+startvar+newlen,zeexpression+startvar+ivar,lenw-startvar-ivar+1);
+ }
+ strncpy(zeexpression+startvar,ae->alias[ialias].translation,newlen); /* copy without zero terminator */
+ idx=startvar;
+ ivar=0;
+ continue;
+ } else {
+ /* index possible sur une struct? */
+ int reverse_idx,validx=-1;
+ char *structlabel;
+
+ reverse_idx=strlen(ae->computectx->varbuffer)-1;
+ if (ae->computectx->varbuffer[reverse_idx]>='0' && ae->computectx->varbuffer[reverse_idx]<='9') {
+ /* vu que ça ne PEUT PAS être une valeur litérale, on ne fait pas de test de débordement */
+ reverse_idx--;
+ while (ae->computectx->varbuffer[reverse_idx]>='0' && ae->computectx->varbuffer[reverse_idx]<='9') {
+ reverse_idx--;
+ }
+ reverse_idx++;
+ validx=atoi(ae->computectx->varbuffer+reverse_idx);
+ structlabel=TxtStrDup(ae->computectx->varbuffer+minusptr);
+ structlabel[reverse_idx-minusptr]=0;
+#ifdef TRACE_STRUCT
+ printf("EVOL 119 -> looking for struct %s IDX=%d\n",structlabel,validx);
+#endif
+ /* unoptimized search in structures aliases */
+ crc=GetCRC(structlabel);
+ for (i=0;i<ae->irasmstructalias;i++) {
+ if (ae->rasmstructalias[i].crc==crc && strcmp(ae->rasmstructalias[i].name,structlabel)==0) {
+#ifdef TRACE_STRUCT
+ printf("EVOL 119 -> found! ptr=%d size=%d\n",ae->rasmstructalias[i].ptr,ae->rasmstructalias[i].size);
+#endif
+ curval=ae->rasmstructalias[i].size*validx+ae->rasmstructalias[i].ptr;
+ if (validx>=ae->rasmstructalias[i].nbelem) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: index out of array size!\n",GetExpFile(ae,didx),GetExpLine(ae,didx));
+ }
+ break;
+ }
+ }
+ if (i==ae->irasmstructalias) {
+ /* not found */
+ validx=-1;
+ }
+ MemFree(structlabel);
+ }
+ if (validx<0) {
+ /* last chance to get a keyword */
+ if (strcmp(ae->computectx->varbuffer+minusptr,"REPEAT_COUNTER")==0) {
+ if (ae->ir) {
+ curval=ae->repeat[ae->ir-1].repeat_counter;
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"cannot use REPEAT_COUNTER keyword outside a repeat loop\n");
+ curval=0;
+ }
+ } else if (strcmp(ae->computectx->varbuffer+minusptr,"WHILE_COUNTER")==0) {
+ if (ae->iw) {
+ curval=ae->whilewend[ae->iw-1].while_counter;
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"cannot use WHILE_COUNTER keyword outside a while loop\n");
+ curval=0;
+ }
+ } else {
+ /* in case the expression is a register */
+ if (IsRegister(ae->computectx->varbuffer+minusptr)) {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"cannot use register %s in this context\n",TradExpression(zeexpression));
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] keyword [%s] not found in variables, labels or aliases\n",TradExpression(zeexpression),ae->computectx->varbuffer+minusptr);
+ if (ae->extended_error) {
+ char *lookstr;
+ lookstr=StringLooksLike(ae,ae->computectx->varbuffer+minusptr);
+ if (lookstr) {
+ rasm_printf(ae,KERROR" did you mean [%s] ?\n",lookstr);
+ }
+ }
+ }
+
+ curval=0;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (minusptr) curval=-curval;
+ stackelement.operator=E_COMPUTE_OPERATION_PUSH_DATASTC;
+ stackelement.value=curval;
+ /* priority isn't used */
+
+ allow_minus_as_sign=0;
+ ivar=0;
+ }
+ /************************************
+ C R E A T E T O K E N
+ ************************************/
+ ObjectArrayAddDynamicValueConcat((void **)&ae->computectx->tokenstack,&nbtokenstack,&ae->computectx->maxtokenstack,&stackelement,sizeof(stackelement));
+ }
+ /*******************************************************
+ C R E A T E E X E C U T I O N S T A C K
+ *******************************************************/
+#define DEBUG_STACK 0
+#if DEBUG_STACK
+ for (itoken=0;itoken<nbtokenstack;itoken++) {
+ switch (ae->computectx->tokenstack[itoken].operator) {
+ case E_COMPUTE_OPERATION_PUSH_DATASTC:printf("%lf ",ae->computectx->tokenstack[itoken].value);break;
+ case E_COMPUTE_OPERATION_OPEN:printf("(");break;
+ case E_COMPUTE_OPERATION_CLOSE:printf(")");break;
+ case E_COMPUTE_OPERATION_ADD:printf("+ ");break;
+ case E_COMPUTE_OPERATION_SUB:printf("- ");break;
+ case E_COMPUTE_OPERATION_DIV:printf("/ ");break;
+ case E_COMPUTE_OPERATION_MUL:printf("* ");break;
+ case E_COMPUTE_OPERATION_AND:printf("and ");break;
+ case E_COMPUTE_OPERATION_OR:printf("or ");break;
+ case E_COMPUTE_OPERATION_MOD:printf("mod ");break;
+ case E_COMPUTE_OPERATION_XOR:printf("xor ");break;
+ case E_COMPUTE_OPERATION_NOT:printf("! ");break;
+ case E_COMPUTE_OPERATION_SHL:printf("<< ");break;
+ case E_COMPUTE_OPERATION_SHR:printf(">> ");break;
+ case E_COMPUTE_OPERATION_BAND:printf("&& ");break;
+ case E_COMPUTE_OPERATION_BOR:printf("|| ");break;
+ case E_COMPUTE_OPERATION_LOWER:printf("< ");break;
+ case E_COMPUTE_OPERATION_GREATER:printf("> ");break;
+ case E_COMPUTE_OPERATION_EQUAL:printf("== ");break;
+ case E_COMPUTE_OPERATION_NOTEQUAL:printf("!= ");break;
+ case E_COMPUTE_OPERATION_LOWEREQ:printf("<= ");break;
+ case E_COMPUTE_OPERATION_GREATEREQ:printf(">= ");break;
+ case E_COMPUTE_OPERATION_SIN:printf("sin ");break;
+ case E_COMPUTE_OPERATION_COS:printf("cos ");break;
+ case E_COMPUTE_OPERATION_INT:printf("int ");break;
+ case E_COMPUTE_OPERATION_FLOOR:printf("floor ");break;
+ case E_COMPUTE_OPERATION_ABS:printf("abs ");break;
+ case E_COMPUTE_OPERATION_LN:printf("ln ");break;
+ case E_COMPUTE_OPERATION_LOG10:printf("log10 ");break;
+ case E_COMPUTE_OPERATION_SQRT:printf("sqrt ");break;
+ case E_COMPUTE_OPERATION_ASIN:printf("asin ");break;
+ case E_COMPUTE_OPERATION_ACOS:printf("acos ");break;
+ case E_COMPUTE_OPERATION_ATAN:printf("atan ");break;
+ case E_COMPUTE_OPERATION_EXP:printf("exp ");break;
+ case E_COMPUTE_OPERATION_LOW:printf("low ");break;
+ case E_COMPUTE_OPERATION_HIGH:printf("high ");break;
+ case E_COMPUTE_OPERATION_PSG:printf("psg ");break;
+ case E_COMPUTE_OPERATION_RND:printf("rnd ");break;
+ case E_COMPUTE_OPERATION_FRAC:printf("frac ");break;
+ case E_COMPUTE_OPERATION_CEIL:printf("ceil ");break;
+ case E_COMPUTE_OPERATION_GET_R:printf("get_r ");break;
+ case E_COMPUTE_OPERATION_GET_V:printf("get_v ");break;
+ case E_COMPUTE_OPERATION_GET_B:printf("get_b ");break;
+ case E_COMPUTE_OPERATION_SET_R:printf("set_r ");break;
+ case E_COMPUTE_OPERATION_SET_V:printf("set_v ");break;
+ case E_COMPUTE_OPERATION_SET_B:printf("set_b ");break;
+ default:printf("bug\n");break;
+ }
+
+ }
+ printf("\n");
+#endif
+
+ for (itoken=0;itoken<nbtokenstack;itoken++) {
+ switch (ae->computectx->tokenstack[itoken].operator) {
+ case E_COMPUTE_OPERATION_PUSH_DATASTC:
+#if DEBUG_STACK
+printf("data\n");
+#endif
+ ObjectArrayAddDynamicValueConcat((void **)&computestack,&nbcomputestack,&maxcomputestack,&ae->computectx->tokenstack[itoken],sizeof(stackelement));
+ break;
+ case E_COMPUTE_OPERATION_OPEN:
+ ObjectArrayAddDynamicValueConcat((void **)&ae->computectx->operatorstack,&nboperatorstack,&ae->computectx->maxoperatorstack,&ae->computectx->tokenstack[itoken],sizeof(stackelement));
+#if DEBUG_STACK
+printf("ajout (\n");
+#endif
+ break;
+ case E_COMPUTE_OPERATION_CLOSE:
+#if DEBUG_STACK
+printf("close\n");
+#endif
+ /* pop out token until the opened parenthesis is reached */
+ o2=nboperatorstack-1;
+ okclose=0;
+ while (o2>=0) {
+ if (ae->computectx->operatorstack[o2].operator!=E_COMPUTE_OPERATION_OPEN) {
+ ObjectArrayAddDynamicValueConcat((void **)&computestack,&nbcomputestack,&maxcomputestack,&ae->computectx->operatorstack[o2],sizeof(stackelement));
+ nboperatorstack--;
+#if DEBUG_STACK
+printf("op--\n");
+#endif
+ o2--;
+ } else {
+ /* discard opening parenthesis as operator */
+#if DEBUG_STACK
+printf("discard )\n");
+#endif
+ nboperatorstack--;
+ okclose=1;
+ o2--;
+ break;
+ }
+ }
+ if (!okclose) {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"missing parenthesis [%s]\n",TradExpression(zeexpression));
+ if (!original) {
+ MemFree(zeexpression);
+ }
+ return 0;
+ }
+ /* if upper token is a function then pop from the stack */
+ if (o2>=0 && ae->computectx->operatorstack[o2].operator>=E_COMPUTE_OPERATION_SIN) {
+ ObjectArrayAddDynamicValueConcat((void **)&computestack,&nbcomputestack,&maxcomputestack,&ae->computectx->operatorstack[o2],sizeof(stackelement));
+ nboperatorstack--;
+#if DEBUG_STACK
+printf("pop function\n");
+#endif
+ }
+ break;
+ case E_COMPUTE_OPERATION_ADD:
+ case E_COMPUTE_OPERATION_SUB:
+ case E_COMPUTE_OPERATION_DIV:
+ case E_COMPUTE_OPERATION_MUL:
+ case E_COMPUTE_OPERATION_AND:
+ case E_COMPUTE_OPERATION_OR:
+ case E_COMPUTE_OPERATION_MOD:
+ case E_COMPUTE_OPERATION_XOR:
+ case E_COMPUTE_OPERATION_NOT:
+ case E_COMPUTE_OPERATION_SHL:
+ case E_COMPUTE_OPERATION_SHR:
+ case E_COMPUTE_OPERATION_BAND:
+ case E_COMPUTE_OPERATION_BOR:
+ case E_COMPUTE_OPERATION_LOWER:
+ case E_COMPUTE_OPERATION_GREATER:
+ case E_COMPUTE_OPERATION_EQUAL:
+ case E_COMPUTE_OPERATION_NOTEQUAL:
+ case E_COMPUTE_OPERATION_LOWEREQ:
+ case E_COMPUTE_OPERATION_GREATEREQ:
+#if DEBUG_STACK
+printf("operator\n");
+#endif
+ o2=nboperatorstack-1;
+ while (o2>=0 && ae->computectx->operatorstack[o2].operator!=E_COMPUTE_OPERATION_OPEN) {
+ if (ae->computectx->tokenstack[itoken].priority>=ae->computectx->operatorstack[o2].priority || ae->computectx->operatorstack[o2].operator>=E_COMPUTE_OPERATION_SIN) {
+ ObjectArrayAddDynamicValueConcat((void **)&computestack,&nbcomputestack,&maxcomputestack,&ae->computectx->operatorstack[o2],sizeof(stackelement));
+ nboperatorstack--;
+ o2--;
+ } else {
+ break;
+ }
+ }
+ ObjectArrayAddDynamicValueConcat((void **)&ae->computectx->operatorstack,&nboperatorstack,&ae->computectx->maxoperatorstack,&ae->computectx->tokenstack[itoken],sizeof(stackelement));
+ break;
+ case E_COMPUTE_OPERATION_SIN:
+ case E_COMPUTE_OPERATION_COS:
+ case E_COMPUTE_OPERATION_INT:
+ case E_COMPUTE_OPERATION_FLOOR:
+ case E_COMPUTE_OPERATION_ABS:
+ case E_COMPUTE_OPERATION_LN:
+ case E_COMPUTE_OPERATION_LOG10:
+ case E_COMPUTE_OPERATION_SQRT:
+ case E_COMPUTE_OPERATION_ASIN:
+ case E_COMPUTE_OPERATION_ACOS:
+ case E_COMPUTE_OPERATION_ATAN:
+ case E_COMPUTE_OPERATION_EXP:
+ case E_COMPUTE_OPERATION_LOW:
+ case E_COMPUTE_OPERATION_HIGH:
+ case E_COMPUTE_OPERATION_PSG:
+ case E_COMPUTE_OPERATION_RND:
+ case E_COMPUTE_OPERATION_FRAC:
+ case E_COMPUTE_OPERATION_CEIL:
+ case E_COMPUTE_OPERATION_GET_R:
+ case E_COMPUTE_OPERATION_GET_V:
+ case E_COMPUTE_OPERATION_GET_B:
+ case E_COMPUTE_OPERATION_SET_R:
+ case E_COMPUTE_OPERATION_SET_V:
+ case E_COMPUTE_OPERATION_SET_B:
+#if DEBUG_STACK
+printf("ajout de la fonction\n");
+#endif
+ ObjectArrayAddDynamicValueConcat((void **)&ae->computectx->operatorstack,&nboperatorstack,&ae->computectx->maxoperatorstack,&ae->computectx->tokenstack[itoken],sizeof(stackelement));
+ break;
+ default:break;
+ }
+ }
+ /* pop remaining operators */
+ while (nboperatorstack>0) {
+ ObjectArrayAddDynamicValueConcat((void **)&computestack,&nbcomputestack,&maxcomputestack,&ae->computectx->operatorstack[--nboperatorstack],sizeof(stackelement));
+ }
+
+ /********************************************
+ E X E C U T E S T A C K
+ ********************************************/
+ if (ae->maxam || ae->as80) {
+ int workinterval;
+ if (ae->as80) workinterval=0xFFFFFFFF; else workinterval=0xFFFF;
+ for (i=0;i<nbcomputestack;i++) {
+ switch (computestack[i].operator) {
+ /************************************************
+ c a s e s s h o u l d b e s o r t e d
+ ************************************************/
+ case E_COMPUTE_OPERATION_PUSH_DATASTC:
+ if (maccu<=paccu) {
+ maccu=16+paccu;
+ accu=MemRealloc(accu,sizeof(double)*maccu);
+ }
+ accu[paccu]=computestack[i].value;paccu++;
+ break;
+ case E_COMPUTE_OPERATION_OPEN:
+ case E_COMPUTE_OPERATION_CLOSE:/* cannot happend */ break;
+ case E_COMPUTE_OPERATION_ADD:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]+(int)accu[paccu-1])&workinterval;paccu--;break;
+ case E_COMPUTE_OPERATION_SUB:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]-(int)accu[paccu-1])&workinterval;paccu--;break;
+ case E_COMPUTE_OPERATION_MUL:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]*(int)accu[paccu-1])&workinterval;paccu--;break;
+ case E_COMPUTE_OPERATION_DIV:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]/(int)accu[paccu-1])&workinterval;paccu--;break;
+ case E_COMPUTE_OPERATION_AND:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&(int)accu[paccu-1])&workinterval;paccu--;break;
+ case E_COMPUTE_OPERATION_OR:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]|(int)accu[paccu-1])&workinterval;paccu--;break;
+ case E_COMPUTE_OPERATION_XOR:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]^(int)accu[paccu-1])&workinterval;paccu--;break;
+ case E_COMPUTE_OPERATION_MOD:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]%(int)accu[paccu-1])&workinterval;paccu--;break;
+ case E_COMPUTE_OPERATION_SHL:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2])<<((int)accu[paccu-1]);
+ if (((int)accu[paccu-1])>31 || ((int)accu[paccu-1])<-31) {
+ if (!ae->nowarning) {
+ rasm_printf(ae,KWARNING"Warning - shifting %d is architecture dependant, result forced to ZERO\n",(int)accu[paccu-1]);
+ }
+ accu[paccu-2]=0;
+ }
+ paccu--;break;
+ case E_COMPUTE_OPERATION_SHR:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2])>>((int)accu[paccu-1]);
+ if (((int)accu[paccu-1])>31 || ((int)accu[paccu-1])<-31) {
+ if (!ae->nowarning) {
+ rasm_printf(ae,KWARNING"Warning - shifting %d is architecture dependant, result forced to ZERO\n",(int)accu[paccu-1]);
+ }
+ accu[paccu-2]=0;
+ }
+ paccu--;break;
+ case E_COMPUTE_OPERATION_BAND:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&&(int)accu[paccu-1])&workinterval;paccu--;break;
+ case E_COMPUTE_OPERATION_BOR:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]||(int)accu[paccu-1])&workinterval;paccu--;break;
+ /* comparison */
+ case E_COMPUTE_OPERATION_LOWER:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&workinterval)<((int)accu[paccu-1]&workinterval);paccu--;break;
+ case E_COMPUTE_OPERATION_LOWEREQ:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&workinterval)<=((int)accu[paccu-1]&workinterval);paccu--;break;
+ case E_COMPUTE_OPERATION_EQUAL:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&workinterval)==((int)accu[paccu-1]&workinterval);paccu--;break;
+ case E_COMPUTE_OPERATION_NOTEQUAL:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&workinterval)!=((int)accu[paccu-1]&workinterval);paccu--;break;
+ case E_COMPUTE_OPERATION_GREATER:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&workinterval)>((int)accu[paccu-1]&workinterval);paccu--;break;
+ case E_COMPUTE_OPERATION_GREATEREQ:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&workinterval)>=((int)accu[paccu-1]&workinterval);paccu--;break;
+ /* functions */
+ case E_COMPUTE_OPERATION_SIN:if (paccu>0) accu[paccu-1]=(int)sin(accu[paccu-1]*3.1415926545/180.0);break;
+ case E_COMPUTE_OPERATION_COS:if (paccu>0) accu[paccu-1]=(int)cos(accu[paccu-1]*3.1415926545/180.0);break;
+ case E_COMPUTE_OPERATION_ASIN:if (paccu>0) accu[paccu-1]=(int)asin(accu[paccu-1])*180.0/3.1415926545;break;
+ case E_COMPUTE_OPERATION_ACOS:if (paccu>0) accu[paccu-1]=(int)acos(accu[paccu-1])*180.0/3.1415926545;break;
+ case E_COMPUTE_OPERATION_ATAN:if (paccu>0) accu[paccu-1]=(int)atan(accu[paccu-1])*180.0/3.1415926545;break;
+ case E_COMPUTE_OPERATION_INT:break;
+ case E_COMPUTE_OPERATION_FLOOR:if (paccu>0) accu[paccu-1]=(int)floor(accu[paccu-1])&workinterval;break;
+ case E_COMPUTE_OPERATION_ABS:if (paccu>0) accu[paccu-1]=(int)fabs(accu[paccu-1])&workinterval;break;
+ case E_COMPUTE_OPERATION_EXP:if (paccu>0) accu[paccu-1]=(int)exp(accu[paccu-1])&workinterval;break;
+ case E_COMPUTE_OPERATION_LN:if (paccu>0) accu[paccu-1]=(int)log(accu[paccu-1])&workinterval;break;
+ case E_COMPUTE_OPERATION_LOG10:if (paccu>0) accu[paccu-1]=(int)log10(accu[paccu-1])&workinterval;break;
+ case E_COMPUTE_OPERATION_SQRT:if (paccu>0) accu[paccu-1]=(int)sqrt(accu[paccu-1])&workinterval;break;
+ case E_COMPUTE_OPERATION_LOW:if (paccu>0) accu[paccu-1]=((int)accu[paccu-1])&0xFF;break;
+ case E_COMPUTE_OPERATION_HIGH:if (paccu>0) accu[paccu-1]=(((int)accu[paccu-1])&0xFF00)>>8;break;
+ case E_COMPUTE_OPERATION_PSG:if (paccu>0) accu[paccu-1]=ae->psgfine[((int)accu[paccu-1])&0xFF];break;
+ case E_COMPUTE_OPERATION_RND:if (paccu>0) accu[paccu-1]=rand()%((int)accu[paccu-1]);break;
+ case E_COMPUTE_OPERATION_FRAC:if (paccu>0) accu[paccu-1]=((int)(accu[paccu-1]-(int)accu[paccu-1]));break;
+ case E_COMPUTE_OPERATION_CEIL:if (paccu>0) accu[paccu-1]=(int)ceil(accu[paccu-1])&workinterval;break;
+ case E_COMPUTE_OPERATION_GET_R:if (paccu>0) accu[paccu-1]=((((int)accu[paccu-1])&0xF0)>>4);break;
+ case E_COMPUTE_OPERATION_GET_V:if (paccu>0) accu[paccu-1]=((((int)accu[paccu-1])&0xF00)>>8);break;
+ case E_COMPUTE_OPERATION_GET_B:if (paccu>0) accu[paccu-1]=(((int)accu[paccu-1])&0xF);break;
+ case E_COMPUTE_OPERATION_SET_R:if (paccu>0) accu[paccu-1]=MinMaxInt(accu[paccu-1],0,15)<<4;break;
+ case E_COMPUTE_OPERATION_SET_V:if (paccu>0) accu[paccu-1]=MinMaxInt(accu[paccu-1],0,15)<<8;break;
+ case E_COMPUTE_OPERATION_SET_B:if (paccu>0) accu[paccu-1]=MinMaxInt(accu[paccu-1],0,15);break;
+ default:MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"invalid computing state! (%d)\n",GetExpFile(ae,didx),GetExpLine(ae,didx),computestack[i].operator);paccu=0;
+ }
+ if (!paccu) {
+ if (zeexpression[0]=='&') {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Missing operand for calculation [%s] Did you use & for an hexadecimal value?\n",TradExpression(zeexpression));
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Missing operand for calculation [%s]\n",TradExpression(zeexpression));
+ }
+ accu_err=1;
+ break;
+ }
+ }
+ } else {
+ for (i=0;i<nbcomputestack;i++) {
+#if 0
+ int kk;
+ for (kk=0;kk<paccu;kk++) printf("stack[%d]=%lf\n",kk,accu[kk]);
+ if (computestack[i].operator==E_COMPUTE_OPERATION_PUSH_DATASTC) {
+ printf("pacc=%d push %.1lf\n",paccu,computestack[i].value);
+ } else {
+ printf("pacc=%d operation %s p=%d\n",paccu,computestack[i].operator==E_COMPUTE_OPERATION_MUL?"*":
+ computestack[i].operator==E_COMPUTE_OPERATION_ADD?"+":
+ computestack[i].operator==E_COMPUTE_OPERATION_DIV?"/":
+ computestack[i].operator==E_COMPUTE_OPERATION_SUB?"-":
+ computestack[i].operator==E_COMPUTE_OPERATION_BAND?"&&":
+ computestack[i].operator==E_COMPUTE_OPERATION_BOR?"||":
+ computestack[i].operator==E_COMPUTE_OPERATION_SHL?"<<":
+ computestack[i].operator==E_COMPUTE_OPERATION_SHR?">>":
+ computestack[i].operator==E_COMPUTE_OPERATION_LOWER?"<":
+ computestack[i].operator==E_COMPUTE_OPERATION_GREATER?">":
+ computestack[i].operator==E_COMPUTE_OPERATION_EQUAL?"==":
+ computestack[i].operator==E_COMPUTE_OPERATION_INT?"INT":
+ computestack[i].operator==E_COMPUTE_OPERATION_LOWEREQ?"<=":
+ computestack[i].operator==E_COMPUTE_OPERATION_GREATEREQ?">=":
+ computestack[i].operator==E_COMPUTE_OPERATION_OPEN?"(":
+ computestack[i].operator==E_COMPUTE_OPERATION_CLOSE?")":
+ "<autre>",computestack[i].priority);
+ }
+#endif
+ switch (computestack[i].operator) {
+ case E_COMPUTE_OPERATION_PUSH_DATASTC:
+ if (maccu<=paccu) {
+ maccu=16+paccu;
+ accu=MemRealloc(accu,sizeof(double)*maccu);
+ }
+ accu[paccu]=computestack[i].value;paccu++;
+ break;
+ case E_COMPUTE_OPERATION_OPEN:
+ case E_COMPUTE_OPERATION_CLOSE: /* cannot happend */ break;
+ case E_COMPUTE_OPERATION_ADD:if (paccu>1) accu[paccu-2]+=accu[paccu-1];paccu--;break;
+ case E_COMPUTE_OPERATION_SUB:if (paccu>1) accu[paccu-2]-=accu[paccu-1];paccu--;break;
+ case E_COMPUTE_OPERATION_MUL:if (paccu>1) accu[paccu-2]*=accu[paccu-1];paccu--;break;
+ case E_COMPUTE_OPERATION_DIV:if (paccu>1) accu[paccu-2]/=accu[paccu-1];paccu--;break;
+ case E_COMPUTE_OPERATION_AND:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))&((int)floor(accu[paccu-1]+0.5));paccu--;break;
+ case E_COMPUTE_OPERATION_OR:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))|((int)floor(accu[paccu-1]+0.5));paccu--;break;
+ case E_COMPUTE_OPERATION_XOR:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))^((int)floor(accu[paccu-1]+0.5));paccu--;break;
+ case E_COMPUTE_OPERATION_NOT:/* half operator, half function */ if (paccu>0) accu[paccu-1]=!((int)floor(accu[paccu-1]+0.5));break;
+ case E_COMPUTE_OPERATION_MOD:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))%((int)floor(accu[paccu-1]+0.5));paccu--;break;
+ case E_COMPUTE_OPERATION_SHL:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))<<((int)floor(accu[paccu-1]+0.5));
+ if (((int)accu[paccu-1])>31 || ((int)accu[paccu-1])<-31) {
+ if (!ae->nowarning) {
+ rasm_printf(ae,KWARNING"Warning - shifting %d is architecture dependant, result forced to ZERO\n",(int)accu[paccu-1]);
+ }
+ accu[paccu-2]=0;
+ }
+ paccu--;break;
+ case E_COMPUTE_OPERATION_SHR:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))>>((int)floor(accu[paccu-1]+0.5));
+ if (((int)accu[paccu-1])>31 || ((int)accu[paccu-1])<-31) {
+ if (!ae->nowarning) {
+ rasm_printf(ae,KWARNING"Warning - shifting %d is architecture dependant, result forced to ZERO\n",(int)accu[paccu-1]);
+ }
+ accu[paccu-2]=0;
+ }
+ paccu--;break;
+ case E_COMPUTE_OPERATION_BAND:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))&&((int)floor(accu[paccu-1]+0.5));paccu--;break;
+ case E_COMPUTE_OPERATION_BOR:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))||((int)floor(accu[paccu-1]+0.5));paccu--;break;
+ /* comparison */
+ case E_COMPUTE_OPERATION_LOWER:if (paccu>1) accu[paccu-2]=accu[paccu-2]<accu[paccu-1];paccu--;break;
+ case E_COMPUTE_OPERATION_LOWEREQ:if (paccu>1) accu[paccu-2]=accu[paccu-2]<=accu[paccu-1];paccu--;break;
+ case E_COMPUTE_OPERATION_EQUAL:if (paccu>1) accu[paccu-2]=fabs(accu[paccu-2]-accu[paccu-1])<0.000001;paccu--;break;
+ case E_COMPUTE_OPERATION_NOTEQUAL:if (paccu>1) accu[paccu-2]=accu[paccu-2]!=accu[paccu-1];paccu--;break;
+ case E_COMPUTE_OPERATION_GREATER:if (paccu>1) accu[paccu-2]=accu[paccu-2]>accu[paccu-1];paccu--;break;
+ case E_COMPUTE_OPERATION_GREATEREQ:if (paccu>1) accu[paccu-2]=accu[paccu-2]>=accu[paccu-1];paccu--;break;
+ /* functions */
+ case E_COMPUTE_OPERATION_SIN:if (paccu>0) accu[paccu-1]=sin(accu[paccu-1]*3.1415926545/180.0);break;
+ case E_COMPUTE_OPERATION_COS:if (paccu>0) accu[paccu-1]=cos(accu[paccu-1]*3.1415926545/180.0);break;
+ case E_COMPUTE_OPERATION_ASIN:if (paccu>0) accu[paccu-1]=asin(accu[paccu-1])*180.0/3.1415926545;break;
+ case E_COMPUTE_OPERATION_ACOS:if (paccu>0) accu[paccu-1]=acos(accu[paccu-1])*180.0/3.1415926545;break;
+ case E_COMPUTE_OPERATION_ATAN:if (paccu>0) accu[paccu-1]=atan(accu[paccu-1])*180.0/3.1415926545;break;
+ case E_COMPUTE_OPERATION_INT:if (paccu>0) accu[paccu-1]=floor(accu[paccu-1]+0.5);break;
+ case E_COMPUTE_OPERATION_FLOOR:if (paccu>0) accu[paccu-1]=floor(accu[paccu-1]);break;
+ case E_COMPUTE_OPERATION_ABS:if (paccu>0) accu[paccu-1]=fabs(accu[paccu-1]);break;
+ case E_COMPUTE_OPERATION_EXP:if (paccu>0) accu[paccu-1]=exp(accu[paccu-1]);break;
+ case E_COMPUTE_OPERATION_LN:if (paccu>0) accu[paccu-1]=log(accu[paccu-1]);break;
+ case E_COMPUTE_OPERATION_LOG10:if (paccu>0) accu[paccu-1]=log10(accu[paccu-1]);break;
+ case E_COMPUTE_OPERATION_SQRT:if (paccu>0) accu[paccu-1]=sqrt(accu[paccu-1]);break;
+ case E_COMPUTE_OPERATION_LOW:if (paccu>0) accu[paccu-1]=((int)floor(accu[paccu-1]+0.5))&0xFF;break;
+ case E_COMPUTE_OPERATION_HIGH:if (paccu>0) accu[paccu-1]=(((int)floor(accu[paccu-1]+0.5))&0xFF00)>>8;break;
+ case E_COMPUTE_OPERATION_PSG:if (paccu>0) accu[paccu-1]=ae->psgfine[((int)floor(accu[paccu-1]+0.5))&0xFF];break;
+ case E_COMPUTE_OPERATION_RND:if (paccu>0) accu[paccu-1]=rand()%((int)accu[paccu-1]);break;
+ case E_COMPUTE_OPERATION_FRAC:if (paccu>0) accu[paccu-1]=modf(accu[paccu-1],&dummint);break;
+ case E_COMPUTE_OPERATION_CEIL:if (paccu>0) accu[paccu-1]=ceil(accu[paccu-1]);break;
+ case E_COMPUTE_OPERATION_GET_R:if (paccu>0) accu[paccu-1]=((((int)accu[paccu-1])&0xF0)>>4);break;
+ case E_COMPUTE_OPERATION_GET_V:if (paccu>0) accu[paccu-1]=((((int)accu[paccu-1])&0xF00)>>8);break;
+ case E_COMPUTE_OPERATION_GET_B:if (paccu>0) accu[paccu-1]=(((int)accu[paccu-1])&0xF);break;
+ case E_COMPUTE_OPERATION_SET_R:if (paccu>0) accu[paccu-1]=MinMaxInt(accu[paccu-1],0,15)<<4;break;
+ case E_COMPUTE_OPERATION_SET_V:if (paccu>0) accu[paccu-1]=MinMaxInt(accu[paccu-1],0,15)<<8;break;
+ case E_COMPUTE_OPERATION_SET_B:if (paccu>0) accu[paccu-1]=MinMaxInt(accu[paccu-1],0,15);break;
+ default:MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"invalid computing state! (%d)\n",GetExpFile(ae,didx),GetExpLine(ae,didx),computestack[i].operator);paccu=0;
+ }
+ if (!paccu) {
+ if (zeexpression[0]=='&') {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Missing operand for calculation [%s] Did you use & for an hexadecimal value?\n",TradExpression(zeexpression));
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Missing operand for calculation [%s]\n",TradExpression(zeexpression));
+ }
+ accu_err=1;
+ break;
+ }
+ }
+ }
+ if (!original) {
+ MemFree(zeexpression);
+ }
+ if (paccu==1) {
+ return accu[0];
+ } else if (!accu_err) {
+ if (paccu) {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Missing operator\n");
+ } else {
+ MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Missing operand for calculation\n");
+ }
+ return 0;
+ } else {
+ return 0;
+ }
+}
+int RoundComputeExpressionCore(struct s_assenv *ae,char *zeexpression,int ptr,int didx) {
+ return floor(ComputeExpressionCore(ae,zeexpression,ptr,didx)+ae->rough);
+}
+
+void ExpressionSetDicoVar(struct s_assenv *ae,char *name, double v)
+{
+ #undef FUNC
+ #define FUNC "ExpressionSetDicoVar"
+
+ struct s_expr_dico curdic;
+ curdic.name=TxtStrDup(name);
+ curdic.crc=GetCRC(name);
+ curdic.v=v;
+ curdic.iw=ae->idx;
+ curdic.autorise_export=ae->autorise_export;
+ //ObjectArrayAddDynamicValueConcat((void**)&ae->dico,&ae->idic,&ae->mdic,&curdic,sizeof(curdic));
+ if (SearchLabel(ae,curdic.name,curdic.crc)) {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"cannot create variable [%s] as there is already a label with the same name\n",name);
+ MemFree(curdic.name);
+ return;
+ }
+ InsertDicoToTree(ae,&curdic);
+}
+
+double ComputeExpression(struct s_assenv *ae,char *expr, int ptr, int didx, int expected_eval)
+{
+ #undef FUNC
+ #define FUNC "ComputeExpression"
+
+ char *ptr_exp,*ptr_exp2,backupeval;
+ int crc,idic,idx=0,ialias,touched,hasformula=0;
+ double v,vl;
+ struct s_alias curalias;
+ struct s_expr_dico *curdic;
+ char *minibuffer;
+
+ while (!ae->AutomateExpressionDecision[((int)expr[idx])&0xFF]) idx++;
+
+ switch (ae->AutomateExpressionDecision[((int)expr[idx])&0xFF]) {
+ /*****************************************
+ M A K E A L I A S
+ *****************************************/
+ case '~':
+ memset(&curalias,0,sizeof(curalias));
+ ptr_exp=expr+idx;
+ *ptr_exp=0; // on scinde l'alias de son texte
+ ptr_exp2=ptr_exp+1;
+#if TRACE_COMPUTE_EXPRESSION
+printf("MakeAlias (1) EXPR=[%s EQU %s]\n",expr,ptr_exp2);
+#endif
+
+ /* alias locaux ou de proximité */
+ if (strchr("@.",expr[0])) {
+#if TRACE_COMPUTE_EXPRESSION
+printf("WARNING! alias is local! [%s]\n",expr);
+#endif
+ /* local label creation does not handle formula in tags */
+ curalias.alias=TranslateTag(ae,TxtStrDup(expr),&touched,0,E_TAGOPTION_NONE);
+ curalias.alias=MakeLocalLabel(ae,curalias.alias,NULL);
+ hasformula=1;
+ } else if (strchr(expr,'{')) {
+#if TRACE_COMPUTE_EXPRESSION
+printf("WARNING! alias has tag! [%s]\n",expr);
+#endif
+ /* alias name contains formula */
+ curalias.alias=TranslateTag(ae,TxtStrDup(expr),&touched,0,E_TAGOPTION_NONE);
+#if TRACE_COMPUTE_EXPRESSION
+printf("MakeAlias (2) EXPR=[%s EQU %s]\n",expr,ptr_exp2);
+#endif
+ hasformula=1;
+ } else {
+ curalias.alias=TxtStrDup(expr);
+ }
+ curalias.crc=GetCRC(curalias.alias);
+ if ((ialias=SearchAlias(ae,curalias.crc,curalias.alias))>=0) {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"Duplicate alias [%s]\n",expr);
+ MemFree(curalias.alias);
+ } else if (SearchDico(ae,curalias.alias,curalias.crc)) {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"Alias cannot override existing variable [%s]\n",expr);
+ MemFree(curalias.alias);
+ } else {
+ curalias.translation=MemMalloc(strlen(ptr_exp2)+1+2);
+ sprintf(curalias.translation,"(%s)",ptr_exp2);
+#if TRACE_COMPUTE_EXPRESSION
+printf("MakeAlias (3) EXPR=[%s EQU %s]\n",expr,ptr_exp2);
+printf("alias translation [%s] -> ",curalias.translation);fflush(stdout);
+#endif
+ ExpressionFastTranslate(ae,&curalias.translation,2); // FAST type 2
+#if TRACE_COMPUTE_EXPRESSION
+printf("%s\n",curalias.translation);
+#endif
+ curalias.len=strlen(curalias.translation);
+ curalias.autorise_export=ae->autorise_export;
+ curalias.iw=ae->idx;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->alias,&ae->ialias,&ae->malias,&curalias,sizeof(curalias));
+ CheckAndSortAliases(ae);
+ }
+ *ptr_exp='~'; // on remet l'alias en place
+#if TRACE_COMPUTE_EXPRESSION
+printf("MakeAlias end with alias=[%s]=[%s]\n",curalias.alias,curalias.translation);
+printf("***********\n");
+#endif
+ return 0;
+ /*****************************************
+ S E T V A R
+ *****************************************/
+ case '=':
+ /* patch NOT
+ this is a variable assign if there is no other comparison operator after '='
+ BUT we may have ! which stand for NOT but is also a comparison operator...
+ */
+ if (ae->AutomateExpressionDecision[((int)expr[idx+1])&0xFF]==0 || expr[idx+1]=='!') {
+ if (expected_eval) {
+ if (ae->maxam) {
+ /* maxam mode AND expected a value -> force comparison */
+ } else {
+ /* use of a single '=' but expected a comparison anyway */
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"meaningless use of an expression [%s]\n",expr);
+ return 0;
+ }
+ } else {
+ /* ASSIGN */
+ if ((expr[0]<'A' || expr[0]>'Z') && expr[0]!='_') {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"variable name must begin by a letter or '_' [%s]\n",expr);
+ return 0;
+ } else {
+ char operatorassignment;
+
+ ptr_exp=expr+idx;
+ v=ComputeExpressionCore(ae,ptr_exp+1,ptr,didx);
+ *ptr_exp=0;
+ /* patch operator+assign value */
+ switch (ptr_exp[-1]) {
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '^':
+ case '&':
+ case '|':
+ case '%':
+ case ']':
+ case '[':
+ operatorassignment=ptr_exp[-1];ptr_exp[-1]=0;break;
+ default:operatorassignment=0;break;
+ }
+
+ crc=GetCRC(expr);
+ if ((ialias=SearchAlias(ae,crc,expr))>=0) {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"Variable cannot override existing alias [%s]\n",expr);
+ return 0;
+ }
+#if TRACE_ASSEMBLE
+ printf("try to set [%s] with %lf operatorassignment=%c\n",expr,v,operatorassignment);
+#endif
+ curdic=SearchDico(ae,expr,crc);
+ if (curdic) {
+ switch (operatorassignment) {
+ default:printf("warning remover\n");
+ case 0:curdic->v=v;break;
+ case '+':curdic->v+=v;ptr_exp[-1]='+';break;
+ case '-':curdic->v-=v;ptr_exp[-1]='-';break;
+ case '*':curdic->v*=v;ptr_exp[-1]='*';break;
+ case '/':curdic->v/=v;ptr_exp[-1]='/';break;
+ /* bit operations */
+ case '|':curdic->v=((int)curdic->v)|((int)v);ptr_exp[-1]='|';break;
+ case '&':curdic->v=((int)curdic->v)&((int)v);ptr_exp[-1]='&';break;
+ case '^':curdic->v=((int)curdic->v)^((int)v);ptr_exp[-1]='^';break;
+ case '%':curdic->v=((int)curdic->v)%((int)v);ptr_exp[-1]='%';break;
+ case ']':curdic->v=((int)curdic->v)>>((int)v);ptr_exp[-1]=']';
+ if (v>31 || v<-31) {
+ if (!ae->nowarning) {
+ rasm_printf(ae,KWARNING"Warning - shifting %d is architecture dependant, result forced to ZERO\n",(int)v);
+ }
+ curdic->v=0;
+ }
+ break;
+ case '[':curdic->v=((int)curdic->v)<<((int)v);ptr_exp[-1]='[';
+ if (v>31 || v<-31) {
+ if (!ae->nowarning) {
+ rasm_printf(ae,KWARNING"Warning - shifting %d is architecture dependant, result forced to ZERO\n",(int)v);
+ }
+ curdic->v=0;
+ }
+ break;
+ }
+ } else {
+ switch (operatorassignment) {
+ default: /* cannot do operator on non existing variable */
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"Cannot do an operator assignment on non existing variable [%s]\n",expr);
+ return 0;
+ case 0: /* assign a new variable */
+ ExpressionSetDicoVar(ae,expr,v);
+ break;
+ }
+ }
+ *ptr_exp='=';
+ return v;
+ }
+ }
+ }
+ break;
+ /*****************************************
+ P U R E E X P R E S S I O N
+ *****************************************/
+ default:break;
+ }
+#if TRACE_ASSEMBLE
+printf("pure expression to compute [%s]\n",expr);
+#endif
+ return ComputeExpressionCore(ae,expr,ptr,didx);
+}
+int RoundComputeExpression(struct s_assenv *ae,char *expr, int ptr, int didx, int expression_expected) {
+ return floor(ComputeExpression(ae,expr,ptr,didx,expression_expected)+ae->rough);
+}
+
+/*
+ ExpressionFastTranslate
+
+ purpose: translate all known symbols in an expression (especially variables acting like counters)
+*/
+void ExpressionFastTranslate(struct s_assenv *ae, char **ptr_expr, int fullreplace)
+{
+ #undef FUNC
+ #define FUNC "ExpressionFastTranslate"
+
+ struct s_label *curlabel;
+ struct s_expr_dico *curdic;
+ static char *varbuffer=NULL;
+ static int ivar=0,maxivar=1;
+ char curval[256]={0};
+ int c,lenw=0,idx=0,crc,startvar,newlen,ialias,found_replace,yves,dek,reidx,lenbuf,rlen,tagoffset;
+ double v;
+ char tmpuchar[16];
+ char *expr,*locallabel;
+ int curly=0,curlyflag=0;
+ char *Automate;
+ int recurse=-1,recursecount=0;
+
+ if (!ae || !ptr_expr) {
+ if (varbuffer) MemFree(varbuffer);
+ varbuffer=NULL;
+ maxivar=1;
+ ivar=0;
+ return;
+ }
+ /* be sure to have at least some bytes allocated */
+ StateMachineResizeBuffer(&varbuffer,128,&maxivar);
+ expr=*ptr_expr;
+
+//printf("fast [%s]\n",expr);
+
+ while (!ae->AutomateExpressionDecision[((int)expr[idx])&0xFF]) idx++;
+
+ switch (ae->maxam) {
+ default:
+ case 0: /* full check */
+ if (expr[idx]=='~' || (expr[idx]=='=' && expr[idx+1]!='=')) {reidx=idx+1;break;}
+ reidx=0;
+ break;
+ case 1: /* partial check with maxam */
+ if (expr[idx]=='~') {reidx=idx+1;break;}
+ reidx=0;
+ break;
+ }
+
+ idx=0;
+ /* is there ascii char? */
+ while ((c=expr[idx])!=0) {
+ if (c=='\'' || c=='"') {
+ /* echappement */
+ if (expr[idx+1]=='\\') {
+ if (expr[idx+2] && expr[idx+3]==c) {
+ /* no charset conversion for escaped chars */
+ c=expr[idx+2];
+ switch (c) {
+ case 'b':c='\b';break;
+ case 'v':c='\v';break;
+ case 'f':c='\f';break;
+ case '0':c='\0';break;
+ case 'r':c='\r';break;
+ case 'n':c='\n';break;
+ case 't':c='\t';break;
+ default:break;
+ }
+ sprintf(tmpuchar,"#%03X",c);
+ memcpy(expr+idx,tmpuchar,4);
+ idx+=3;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"expression [%s] - Only single escaped char may be quoted\n",expr);
+ expr[0]=0;
+ return;
+ }
+ } else if (expr[idx+1] && expr[idx+2]==c) {
+ sprintf(tmpuchar,"#%02X",ae->charset[(int)expr[idx+1]]);
+ memcpy(expr+idx,tmpuchar,3);
+ idx+=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"expression [%s] - Only single char may be quoted\n",expr);
+ expr[0]=0;
+ return;
+ }
+ }
+ idx++;
+ }
+
+ idx=reidx;
+ while ((c=expr[idx])!=0) {
+ switch (c) {
+ /* operator / parenthesis */
+ case '!':
+ case '=':
+ case '>':
+ case '<':
+ case '(':
+ case ')':
+ case ']':
+ case '[':
+ case '*':
+ case '/':
+ case '+':
+ case '~':
+ case '-':
+ case '^':
+ case 'm':
+ case '|':
+ case '&':
+ idx++;
+ break;
+ default:
+ startvar=idx;
+ if (ae->AutomateExpressionValidCharFirst[((int)c)&0xFF]) {
+ varbuffer[ivar++]=c;
+ if (c=='{') {
+ /* this is only tag and not a formula */
+ curly++;
+ }
+ StateMachineResizeBuffer(&varbuffer,ivar,&maxivar);
+ idx++;
+ c=expr[idx];
+
+ Automate=ae->AutomateExpressionValidChar;
+ while (Automate[((int)c)&0xFF]) {
+ if (c=='{') {
+ curly++;
+ curlyflag=1;
+ Automate=ae->AutomateExpressionValidCharExtended;
+ } else if (c=='}') {
+ curly--;
+ if (!curly) {
+ Automate=ae->AutomateExpressionValidChar;
+ }
+ }
+ varbuffer[ivar++]=c;
+ StateMachineResizeBuffer(&varbuffer,ivar,&maxivar);
+ idx++;
+ c=expr[idx];
+ }
+ }
+ varbuffer[ivar]=0;
+ if (!ivar) {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"invalid expression [%s] c=[%c] idx=%d\n",expr,c,idx);
+ return;
+ } else if (curly) {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"wrong curly brackets in expression [%s]\n",expr);
+ return;
+ }
+ }
+ if (ivar && (varbuffer[0]<'0' || varbuffer[0]>'9')) {
+ /* numbering var or label */
+ if (curlyflag) {
+ char *minivarbuffer;
+ int touched;
+//printf("ExpressionFastTranslate curly\n");
+ minivarbuffer=TranslateTag(ae,TxtStrDup(varbuffer), &touched,0,E_TAGOPTION_NONE|(fullreplace?0:E_TAGOPTION_PRESERVE));
+ StateMachineResizeBuffer(&varbuffer,strlen(minivarbuffer)+1,&maxivar);
+ strcpy(varbuffer,minivarbuffer);
+ newlen=strlen(varbuffer);
+ lenw=strlen(expr);
+ /* must update source */
+ if (newlen>ivar) {
+ /* realloc bigger */
+ expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1);
+ }
+ if (newlen!=ivar ) {
+ lenw=strlen(expr);
+ MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1);
+ }
+ strncpy(expr+startvar,minivarbuffer,newlen); /* copy without zero terminator */
+ idx=startvar+newlen;
+ /***/
+ MemFree(minivarbuffer);
+ curlyflag=0;
+ /******* ivar must be updated in case of label or alias following ***********/
+ ivar=newlen;
+ }
+
+ /* recherche dans dictionnaire et remplacement */
+ crc=GetCRC(varbuffer);
+ found_replace=0;
+ /* pour les affectations ou les tests conditionnels on ne remplace pas le dico (pour le Push oui par contre!) */
+ if (fullreplace) {
+ if (varbuffer[0]=='$' && !varbuffer[1]) {
+ #ifdef OS_WIN
+ snprintf(curval,sizeof(curval)-1,"%d",ae->codeadr);
+ newlen=strlen(curval);
+ #else
+ newlen=snprintf(curval,sizeof(curval)-1,"%d",ae->codeadr);
+ #endif
+ lenw=strlen(expr);
+ if (newlen>ivar) {
+ /* realloc bigger */
+ expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1);
+ }
+ if (newlen!=ivar ) {
+ MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1);
+ found_replace=1;
+ }
+ strncpy(expr+startvar,curval,newlen); /* copy without zero terminator */
+ idx=startvar+newlen;
+ ivar=0;
+ found_replace=1;
+ } else {
+ curdic=SearchDico(ae,varbuffer,crc);
+ if (curdic) {
+ v=curdic->v;
+//printf("ExpressionFastTranslate (full) -> replace var (%s=%0.1lf)\n",varbuffer,v);
+
+ #ifdef OS_WIN
+ snprintf(curval,sizeof(curval)-1,"%lf",v);
+ newlen=TrimFloatingPointString(curval);
+ #else
+ snprintf(curval,sizeof(curval)-1,"%lf",v);
+ newlen=TrimFloatingPointString(curval);
+ #endif
+ lenw=strlen(expr);
+ if (newlen>ivar) {
+ /* realloc bigger */
+ expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1);
+ }
+ if (newlen!=ivar ) {
+ MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1);
+ }
+ strncpy(expr+startvar,curval,newlen); /* copy without zero terminator */
+ idx=startvar+newlen;
+ ivar=0;
+ found_replace=1;
+ }
+ }
+ }
+ /* on cherche aussi dans les labels existants */
+ if (!found_replace) {
+ curlabel=SearchLabel(ae,varbuffer,crc);
+ if (curlabel) {
+ if (!curlabel->lz || ae->stage>1) {
+ yves=curlabel->ptr;
+
+ #ifdef OS_WIN
+ snprintf(curval,sizeof(curval)-1,"%d",yves);
+ newlen=strlen(curval);
+ #else
+ newlen=snprintf(curval,sizeof(curval)-1,"%d",yves);
+ #endif
+ lenw=strlen(expr);
+ if (newlen>ivar) {
+ /* realloc bigger */
+ expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1);
+ }
+ if (newlen!=ivar ) {
+ MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1);
+ }
+ strncpy(expr+startvar,curval,newlen); /* copy without zero terminator */
+ found_replace=1;
+ idx=startvar+newlen;
+ ivar=0;
+ }
+ }
+ }
+ /* non trouve on cherche dans les alias */
+ if (!found_replace) {
+ if ((ialias=SearchAlias(ae,crc,varbuffer))>=0) {
+ newlen=ae->alias[ialias].len;
+ lenw=strlen(expr);
+ /* infinite replacement check */
+ if (recurse<=startvar) {
+ /* recurse maximum count is a mix of alias len and alias number */
+ if (recursecount>ae->ialias+ae->alias[ialias].len) {
+ if (strchr(expr,'~')!=NULL) *strchr(expr,'~')=0;
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"alias definition of %s has infinite recursivity\n",expr);
+ expr[0]=0; /* avoid some errors due to shitty definition */
+ return;
+ } else {
+ recursecount++;
+ }
+ }
+ if (newlen>ivar) {
+ /* realloc bigger */
+ expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1);
+ }
+ if (newlen!=ivar) {
+ MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1);
+ }
+ strncpy(expr+startvar,ae->alias[ialias].translation,newlen); /* copy without zero terminator */
+ found_replace=1;
+ /* need to parse again alias because of delayed declarations */
+ recurse=startvar;
+ idx=startvar;
+ ivar=0;
+ } else {
+ }
+ }
+ if (!found_replace) {
+ //printf("fasttranslate test local label\n");
+ /* non trouve c'est peut-etre un label local - mais pas de l'octal */
+ if (varbuffer[0]=='@' && (varbuffer[1]<'0' || varbuffer[1]>'9')) {
+ char *zepoint;
+ lenbuf=strlen(varbuffer);
+//printf("MakeLocalLabel(ae,varbuffer,&dek); (1)\n");
+ locallabel=MakeLocalLabel(ae,varbuffer,&dek);
+//printf("exprin =[%s] rlen=%d dek-lenbuf=%d\n",expr,rlen,dek-lenbuf);
+ /*** le grand remplacement ***/
+ /* local to macro or loop */
+ rlen=strlen(expr+startvar+lenbuf)+1;
+ expr=*ptr_expr=MemRealloc(expr,strlen(expr)+dek+1);
+ /* move end of expression in order to insert local ID */
+ zepoint=strchr(varbuffer,'.');
+ if (zepoint) {
+ /* far proximity access */
+ int suffixlen,dotpos;
+ dotpos=(zepoint-varbuffer);
+ suffixlen=lenbuf-dotpos;
+
+ MemMove(expr+startvar+dotpos+dek,expr+startvar+dotpos,rlen+suffixlen);
+ strncpy(expr+startvar+dotpos,locallabel,dek);
+ } else {
+ /* legacy */
+ MemMove(expr+startvar+lenbuf+dek,expr+startvar+lenbuf,rlen);
+ strncpy(expr+startvar+lenbuf,locallabel,dek);
+ }
+ idx+=dek;
+ MemFree(locallabel);
+ found_replace=1;
+//printf("exprout=[%s]\n",expr);
+ } else if (varbuffer[0]=='.' && (varbuffer[1]<'0' || varbuffer[1]>'9')) {
+ /* proximity label */
+ lenbuf=strlen(varbuffer);
+//printf("MakeLocalLabel(ae,varbuffer,&dek); (2)\n");
+ locallabel=MakeLocalLabel(ae,varbuffer,&dek);
+ /*** le grand remplacement ***/
+ rlen=strlen(expr+startvar+lenbuf)+1;
+ dek=strlen(locallabel);
+//printf("exprin =[%s] rlen=%d dek-lenbuf=%d\n",expr,rlen,dek-lenbuf);
+ expr=*ptr_expr=MemRealloc(expr,strlen(expr)+dek-lenbuf+1);
+ MemMove(expr+startvar+dek,expr+startvar+lenbuf,rlen);
+ strncpy(expr+startvar,locallabel,dek);
+ idx+=dek-lenbuf;
+ MemFree(locallabel);
+//printf("exprout=[%s]\n",expr);
+
+//@@TODO ajouter une recherche d'alias?
+
+ } else if (varbuffer[0]=='{') {
+ if (strncmp(varbuffer,"{BANK}",6)==0 || strncmp(varbuffer,"{PAGE}",6)==0) tagoffset=6; else
+ if (strncmp(varbuffer,"{PAGESET}",9)==0) tagoffset=9; else
+ if (strncmp(varbuffer,"{SIZEOF}",8)==0) tagoffset=8; else
+ {
+ tagoffset=0;
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"Unknown prefix tag\n");
+ }
+
+ if (varbuffer[tagoffset]=='@') {
+ char *zepoint;
+ startvar+=tagoffset;
+ lenbuf=strlen(varbuffer+tagoffset);
+//printf("MakeLocalLabel(ae,varbuffer,&dek); (3)\n");
+ locallabel=MakeLocalLabel(ae,varbuffer+tagoffset,&dek);
+ /*** le grand remplacement ***/
+ rlen=strlen(expr+startvar+lenbuf)+1;
+ expr=*ptr_expr=MemRealloc(expr,strlen(expr)+dek+1);
+ /* move end of expression in order to insert local ID */
+ zepoint=strchr(varbuffer,'.');
+ if (zepoint) {
+ /* far proximity access */
+ int suffixlen,dotpos;
+ dotpos=(zepoint-varbuffer);
+ suffixlen=lenbuf-dotpos;
+
+ MemMove(expr+startvar+dotpos+dek,expr+startvar+dotpos,rlen+suffixlen);
+ strncpy(expr+startvar+dotpos,locallabel,dek);
+ } else {
+ /* legacy */
+ MemMove(expr+startvar+lenbuf+dek,expr+startvar+lenbuf,rlen);
+ strncpy(expr+startvar+lenbuf,locallabel,dek);
+ }
+ idx+=dek;
+ MemFree(locallabel);
+ found_replace=1;
+ } else if (varbuffer[tagoffset]=='$') {
+ int tagvalue=-1;
+ if (strcmp(varbuffer,"{BANK}$")==0) {
+ if (ae->forcecpr) {
+ if (ae->activebank<32) {
+ tagvalue=ae->activebank;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"expression [%s] cannot use BANK $ in a temporary space!\n",TradExpression(expr));
+ tagvalue=0;
+ }
+ } else if (ae->forcesnapshot) {
+ if (ae->activebank<BANK_MAX_NUMBER) {
+ /* on autorise le préfixe BANK en snapshot avec une subtilité */
+ if (ae->bankset[ae->activebank>>2]) {
+ tagvalue=ae->activebank+(ae->codeadr>>14); /* dans un bankset on tient compte de l'adresse */
+ } else {
+ tagvalue=ae->activebank;
+ }
+
+ } else {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"expression [%s] cannot use BANK $ in a temporary space!\n",TradExpression(expr));
+ tagvalue=0;
+ }
+ }
+ } else if (strcmp(varbuffer,"{PAGE}$")==0) {
+ if (ae->activebank<BANK_MAX_NUMBER) {
+ if (ae->bankset[ae->activebank>>2]) {
+ tagvalue=ae->bankgate[(ae->activebank&0x1FC)+(ae->codeadr>>14)];
+ } else {
+ tagvalue=ae->bankgate[ae->activebank];
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"expression [%s] cannot use PAGE $ in a temporary space!\n",TradExpression(expr));
+ tagvalue=ae->activebank;
+ }
+ } else if (strcmp(varbuffer,"{PAGESET}$")==0) {
+ if (ae->activebank<BANK_MAX_NUMBER) {
+ tagvalue=ae->setgate[ae->activebank];
+ //if (ae->activebank>3) tagvalue=((ae->activebank>>2)-1)*8+0xC2; else tagvalue=0xC0;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"expression [%s] cannot use PAGESET $ in a temporary space!\n",TradExpression(expr));
+ tagvalue=ae->activebank;
+ }
+ }
+ /* replace */
+ #ifdef OS_WIN
+ snprintf(curval,sizeof(curval)-1,"%d",tagvalue);
+ newlen=strlen(curval);
+ #else
+ newlen=snprintf(curval,sizeof(curval)-1,"%d",tagvalue);
+ #endif
+ lenw=strlen(expr);
+ if (newlen>ivar) {
+ /* realloc bigger */
+ expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1);
+ }
+ if (newlen!=ivar ) {
+ MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1);
+ found_replace=1;
+ }
+ strncpy(expr+startvar,curval,newlen); /* copy without zero terminator */
+ idx=startvar+newlen;
+ ivar=0;
+ found_replace=1;
+ }
+ }
+ }
+
+
+
+
+
+
+ if (!found_replace && strcmp(varbuffer,"REPEAT_COUNTER")==0) {
+ if (ae->ir) {
+ yves=ae->repeat[ae->ir-1].repeat_counter;
+ #ifdef OS_WIN
+ snprintf(curval,sizeof(curval)-1,"%d",yves);
+ newlen=strlen(curval);
+ #else
+ newlen=snprintf(curval,sizeof(curval)-1,"%d",yves);
+ #endif
+ lenw=strlen(expr);
+ if (newlen>ivar) {
+ /* realloc bigger */
+ expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1);
+ }
+ if (newlen!=ivar ) {
+ MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1);
+ found_replace=1;
+ }
+ strncpy(expr+startvar,curval,newlen); /* copy without zero terminator */
+ found_replace=1;
+ idx=startvar+newlen;
+ ivar=0;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"cannot use REPEAT_COUNTER outside repeat loop\n");
+ }
+ }
+ if (!found_replace && strcmp(varbuffer,"WHILE_COUNTER")==0) {
+ if (ae->iw) {
+ yves=ae->whilewend[ae->iw-1].while_counter;
+ #ifdef OS_WIN
+ snprintf(curval,sizeof(curval)-1,"%d",yves);
+ newlen=strlen(curval);
+ #else
+ newlen=snprintf(curval,sizeof(curval)-1,"%d",yves);
+ #endif
+ lenw=strlen(expr);
+ if (newlen>ivar) {
+ /* realloc bigger */
+ expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1);
+ }
+ if (newlen!=ivar ) {
+ MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1);
+ found_replace=1;
+ }
+ strncpy(expr+startvar,curval,newlen); /* copy without zero terminator */
+ found_replace=1;
+ idx=startvar+newlen;
+ ivar=0;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"cannot use WHILE_COUNTER outside repeat loop\n");
+ }
+ }
+ /* unknown symbol -> add to used symbol pool */
+ if (!found_replace) {
+ InsertUsedToTree(ae,varbuffer,crc);
+ }
+ }
+ ivar=0;
+ }
+}
+
+void PushExpression(struct s_assenv *ae,int iw,enum e_expression zetype)
+{
+ #undef FUNC
+ #define FUNC "PushExpression"
+
+ struct s_expression curexp={0};
+ int startptr=0;
+
+ if (!ae->nocode) {
+ curexp.iw=iw;
+ curexp.wptr=ae->outputadr;
+ curexp.zetype=zetype;
+ curexp.ibank=ae->activebank;
+ curexp.iorgzone=ae->io-1;
+ curexp.lz=ae->lz;
+ /* on traduit de suite les variables du dictionnaire pour les boucles et increments
+ SAUF si c'est une affectation */
+ if (!ae->wl[iw].e) {
+ switch (zetype) {
+ case E_EXPRESSION_V16C:
+ /* check non register usage */
+ switch (GetCRC(ae->wl[iw].w)) {
+ case CRC_IX:
+ case CRC_IY:
+ case CRC_MIX:
+ case CRC_MIY:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"invalid register usage\n",ae->maxptr);
+ default:break;
+ }
+ case E_EXPRESSION_J8:
+ case E_EXPRESSION_V8:
+ case E_EXPRESSION_V16:
+ case E_EXPRESSION_IM:startptr=-1;
+ break;
+ case E_EXPRESSION_IV8:
+ case E_EXPRESSION_IV81:
+ case E_EXPRESSION_IV16:startptr=-2;
+ break;
+ case E_EXPRESSION_3V8:startptr=-3;
+ break;
+ case E_EXPRESSION_RUN:
+ case E_EXPRESSION_ZXRUN:
+ case E_EXPRESSION_ZXSTACK:
+ case E_EXPRESSION_BRS:break;
+ default:break;
+ }
+ /* hack pourri pour gérer le $ */
+ ae->codeadr+=startptr;
+ /* ok mais les labels locaux des macros? */
+ if (ae->ir || ae->iw || ae->imacro) {
+ curexp.reference=TxtStrDup(ae->wl[iw].w);
+ ExpressionFastTranslate(ae,&curexp.reference,1);
+ } else {
+ ExpressionFastTranslate(ae,&ae->wl[iw].w,1);
+ }
+ ae->codeadr-=startptr;
+ }
+ /* calcul adresse de reference et post-incrementation pour sauter les data */
+ switch (zetype) {
+ case E_EXPRESSION_J8:curexp.ptr=ae->codeadr-1;ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_0V8:curexp.ptr=ae->codeadr;ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_V8:curexp.ptr=ae->codeadr-1;ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_0V16:curexp.ptr=ae->codeadr;ae->outputadr+=2;ae->codeadr+=2;break;
+ case E_EXPRESSION_0V32:curexp.ptr=ae->codeadr;ae->outputadr+=4;ae->codeadr+=4;break;
+ case E_EXPRESSION_0VR:curexp.ptr=ae->codeadr;ae->outputadr+=5;ae->codeadr+=5;break;
+ case E_EXPRESSION_V16C:
+ case E_EXPRESSION_V16:curexp.ptr=ae->codeadr-1;ae->outputadr+=2;ae->codeadr+=2;break;
+ case E_EXPRESSION_IV81:curexp.ptr=ae->codeadr-2;ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_IV8:curexp.ptr=ae->codeadr-2;ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_3V8:curexp.ptr=ae->codeadr-3;ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_IV16:curexp.ptr=ae->codeadr-2;ae->outputadr+=2;ae->codeadr+=2;break;
+ case E_EXPRESSION_RST:curexp.ptr=ae->codeadr;ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_IM:curexp.ptr=ae->codeadr-1;ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_RUN:break;
+ case E_EXPRESSION_ZXRUN:break;
+ case E_EXPRESSION_ZXSTACK:break;
+ case E_EXPRESSION_BRS:curexp.ptr=ae->codeadr;break; // minimum syndical
+ default:break;
+ }
+ /* le contrôle n'est pas bon avec les DEFB, DEFW, ... -> @@TODO */
+ if (ae->outputadr<=ae->maxptr) {
+ ObjectArrayAddDynamicValueConcat((void **)&ae->expression,&ae->ie,&ae->me,&curexp,sizeof(curexp));
+ } else {
+ /* to avoid double error message */
+ if (!ae->stop) MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"output exceed limit %d\n",ae->maxptr); else MaxError(ae);
+ ae->stop=1;
+ return;
+ }
+ } else {
+ switch (zetype) {
+ case E_EXPRESSION_J8:ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_0V8:ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_V8:ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_0V16:ae->outputadr+=2;ae->codeadr+=2;break;
+ case E_EXPRESSION_0V32:ae->outputadr+=4;ae->codeadr+=4;break;
+ case E_EXPRESSION_0VR:ae->outputadr+=5;ae->codeadr+=5;break;
+ case E_EXPRESSION_V16C:
+ case E_EXPRESSION_V16:ae->outputadr+=2;ae->codeadr+=2;break;
+ case E_EXPRESSION_IV81:ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_IV8:ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_3V8:ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_IV16:ae->outputadr+=2;ae->codeadr+=2;break;
+ case E_EXPRESSION_RST:ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_IM:ae->outputadr++;ae->codeadr++;break;
+ case E_EXPRESSION_RUN:break;
+ case E_EXPRESSION_ZXRUN:break;
+ case E_EXPRESSION_ZXSTACK:break;
+ case E_EXPRESSION_BRS:break;
+ }
+ if (ae->outputadr<=ae->maxptr) {
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"NOCODE output exceed limit %d\n",GetCurrentFile(ae),ae->wl[ae->idx].l,ae->maxptr);
+ FreeAssenv(ae);exit(3);
+ }
+ }
+}
+
+/*
+The CP/M 2.2 directory has only one type of entry:
+
+UU F1 F2 F3 F4 F5 F6 F7 F8 T1 T2 T3 EX S1 S2 RC .FILENAMETYP....
+AL AL AL AL AL AL AL AL AL AL AL AL AL AL AL AL ................
+
+UU = User number. 0-15 (on some systems, 0-31). The user number allows multiple
+ files of the same name to coexist on the disc.
+ User number = 0E5h => File deleted
+Fn - filename
+Tn - filetype. The characters used for these are 7-bit ASCII.
+ The top bit of T1 (often referred to as T1') is set if the file is
+ read-only.
+ T2' is set if the file is a system file (this corresponds to "hidden" on
+ other systems).
+EX = Extent counter, low byte - takes values from 0-31
+S2 = Extent counter, high byte.
+
+ An extent is the portion of a file controlled by one directory entry.
+ If a file takes up more blocks than can be listed in one directory entry,
+ it is given multiple entries, distinguished by their EX and S2 bytes. The
+ formula is: Entry number = ((32*S2)+EX) / (exm+1) where exm is the
+ extent mask value from the Disc Parameter Block.
+
+S1 - reserved, set to 0.
+RC - Number of records (1 record=128 bytes) used in this extent, low byte.
+ The total number of records used in this extent is
+
+ (EX & exm) * 128 + RC
+
+ If RC is 80h, this extent is full and there may be another one on the disc.
+ File lengths are only saved to the nearest 128 bytes.
+
+AL - Allocation. Each AL is the number of a block on the disc. If an AL
+ number is zero, that section of the file has no storage allocated to it
+ (ie it does not exist). For example, a 3k file might have allocation
+ 5,6,8,0,0.... - the first 1k is in block 5, the second in block 6, the
+ third in block 8.
+ AL numbers can either be 8-bit (if there are fewer than 256 blocks on the
+ disc) or 16-bit (stored low byte first).
+*/
+int EDSK_getblockid(int *fb) {
+ #undef FUNC
+ #define FUNC "EDSK_getblockid"
+
+ int i;
+ for (i=0;i<180;i++) {
+ if (fb[i]) {
+ return i;
+ }
+ }
+ return -1;
+}
+int EDSK_getdirid(struct s_edsk_wrapper *curwrap) {
+ #undef FUNC
+ #define FUNC "EDSK_getdirid"
+
+ int ie;
+ for (ie=0;ie<64;ie++) {
+ if (curwrap->entry[ie].user==0xE5) {
+#if TRACE_EDSK
+ printf("getdirid returns %d\n",ie);
+#endif
+ return ie;
+ }
+ }
+ return -1;
+}
+char *MakeAMSDOS_name(struct s_assenv *ae, char *filename)
+{
+ #undef FUNC
+ #define FUNC "MakeAMSDOS_name"
+
+ static char amsdos_name[12];
+ int i,ia;
+ char *pp;
+ /* warning */
+ if (strlen(filename)>12) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"Warning - filename [%s] too long for AMSDOS, will be truncated\n",filename);
+ } else if ((pp=strchr(filename,'.'))!=NULL) {
+ if (pp-filename>8) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"Warning - filename [%s] too long for AMSDOS, will be truncated\n",filename);
+ }
+ }
+ /* copy filename */
+ for (i=0;filename[i]!=0 && filename[i]!='.' && i<8;i++) {
+ amsdos_name[i]=toupper(filename[i]);
+ }
+ /* fill with spaces */
+ for (ia=i;ia<8;ia++) {
+ amsdos_name[ia]=0x20;
+ }
+ /* looking for extension */
+ for (;filename[i]!=0 && filename[i]!='.';i++);
+ /* then copy it if any */
+ if (filename[i]=='.') {
+ i++;
+ for (ia=0;filename[i]!=0 && ia<3;ia++) {
+ amsdos_name[8+ia]=toupper(filename[i++]);
+ }
+ }
+ amsdos_name[11]=0;
+#if TRACE_EDSK
+ printf("MakeAMSDOS_name [%s] -> [%s]\n",filename,amsdos_name);
+#endif
+ return amsdos_name;
+}
+
+
+void EDSK_load(struct s_assenv *ae,struct s_edsk_wrapper *curwrap, char *edskfilename, int face)
+{
+ #undef FUNC
+ #define FUNC "EDSK_load"
+
+ unsigned char header[256];
+ unsigned char *data;
+ int tracknumber,sidenumber,tracksize,disksize;
+ int i,b,s,f,t,curtrack,sectornumber,sectorsize,sectorid,reallength;
+ int currenttrackposition=0,currentsectorposition,tmpcurrentsectorposition;
+ unsigned char checksectorid[9];
+ int curblock=0,curoffset=0;
+#if TRACE_EDSK
+ printf("EDSK_Load('%s',%d);",edskfilename,face);
+#endif
+ if (FileReadBinary(edskfilename,(char*)&header,0x100)!=0x100) {
+ rasm_printf(ae,KERROR"Cannot read EDSK header of [%s]!\n",edskfilename);
+ FreeAssenv(ae);exit(ABORT_ERROR);
+ }
+ if (strncmp((char *)header,"MV - CPC",8)==0) {
+ rasm_printf(ae,KAYGREEN"updating DSK to EDSK [%s] / creator: %s",edskfilename,header+34);
+
+ tracknumber=header[34+14];
+ sidenumber=header[34+14+1];
+ tracksize=header[34+14+1+1]+header[34+14+1+1+1]*256;
+ rasm_printf(ae,"tracks: %d sides:%d track size:%d",tracknumber,sidenumber,tracksize);
+ if (tracknumber>40 || sidenumber>2) {
+ rasm_printf(ae,KERROR"[%s] DSK format is not supported in update mode (ntrack=%d nside=%d)\n",edskfilename,tracknumber,sidenumber);
+ FreeAssenv(ae);exit(ABORT_ERROR);
+ }
+ if (face>=sidenumber) {
+ rasm_printf(ae,KWARNING"[%s] Warning - DSK has no face %d - DSK updated\n",edskfilename,face);
+ return;
+ }
+
+ data=MemMalloc(tracksize*tracknumber*sidenumber);
+ memset(data,0,tracksize*tracknumber*sidenumber);
+ if (FileReadBinary(edskfilename,(char *)data,tracksize*tracknumber*sidenumber)!=tracksize*tracknumber*sidenumber) {
+ rasm_printf(ae,"Cannot read DSK tracks!");
+ FreeAssenv(ae);exit(ABORT_ERROR);
+ }
+ //loginfo("track data read (%dkb)",tracksize*tracknumber*sidenumber/1024);
+ f=face;
+ for (t=0;t<tracknumber;t++) {
+ curtrack=t*sidenumber+f;
+
+ i=(t*sidenumber+f)*tracksize;
+ if (strncmp((char *)data+i,"Track-Info\r\n",12)) {
+ rasm_printf(ae,"Invalid track information block side %d track %d",f,t);
+ FreeAssenv(ae);exit(ABORT_ERROR);
+ }
+ sectornumber=data[i+21];
+ sectorsize=data[i+20];
+ if (sectornumber!=9 || sectorsize!=2) {
+ rasm_printf(ae,"Cannot read [%s] Invalid DATA format",edskfilename);
+ FreeAssenv(ae);exit(ABORT_ERROR);
+ }
+ memset(checksectorid,0,sizeof(checksectorid));
+ /* we want DATA format */
+ for (s=0;s<sectornumber;s++) {
+ if (t!=data[i+24+8*s]) {
+ rasm_printf(ae,"Invalid track number in sector %02X track %d",data[i+24+8*s+2],t);
+ FreeAssenv(ae);exit(ABORT_ERROR);
+ }
+ if (f!=data[i+24+8*s+1]) {
+ rasm_printf(ae,"Invalid side number in sector %02X track %d",data[i+24+8*s+2],t);
+ FreeAssenv(ae);exit(ABORT_ERROR);
+ }
+ if (data[i+24+8*s+2]<0xC1 || data[i+24+8*s+2]>0xC9) {
+ rasm_printf(ae,"Invalid sector ID in sector %02X track %d",data[i+24+8*s+2],t);
+ FreeAssenv(ae);exit(ABORT_ERROR);
+ } else {
+ checksectorid[data[i+24+8*s+2]-0xC1]=1;
+ }
+ if (data[i+24+8*s+3]!=2) {
+ rasm_printf(ae,"Invalid sector size in sector %02X track %d",data[i+24+8*s+2],t);
+ FreeAssenv(ae);exit(ABORT_ERROR);
+ }
+ }
+ for (s=0;s<sectornumber;s++) {
+ if (!checksectorid[s]) {
+ rasm_printf(ae,"Missing sector %02X track %d",s+0xC1,t);
+ FreeAssenv(ae);exit(ABORT_ERROR);
+ }
+ }
+ /* piste à piste on lit les blocs DANS L'ORDRE LOGIQUE!!! */
+ for (b=0xC1;b<=0xC9;b++)
+ for (s=0;s<sectornumber;s++) {
+ if (data[i+24+8*s+2]==b) {
+ memcpy(&curwrap->blocks[curblock][curoffset],&data[i+0x100+s*512],512);
+ curoffset+=512;
+ if (curoffset>=1024) {
+ curoffset=0;
+ curblock++;
+ }
+ }
+ }
+ }
+ } else if (strncmp((char *)header,"EXTENDED",8)==0) {
+ rasm_printf(ae,KAYGREEN"updating EDSK [%s] / creator: %-14.14s\n",edskfilename,header+34);
+ tracknumber=header[34+14];
+ sidenumber=header[34+14+1];
+ // not in EDSK tracksize=header[34+14+1+1]+header[34+14+1+1+1]*256;
+#if TRACE_EDSK
+ loginfo("tracks: %d sides:%d",tracknumber,sidenumber);
+#endif
+
+ if (sidenumber>2) {
+ rasm_printf(ae,KERROR"[%s] EDSK format is not supported in update mode (ntrack=%d nside=%d)\n",edskfilename,tracknumber,sidenumber);
+ FreeAssenv(ae);exit(ABORT_ERROR);
+ }
+ if (face>=sidenumber) {
+ rasm_printf(ae,KWARNING"[%s] EDSK has no face %d - DSK updated\n",edskfilename,face);
+ return;
+ }
+
+ for (i=disksize=0;i<tracknumber*sidenumber;i++) disksize+=header[0x34+i]*256;
+#if TRACE_EDSK
+ loginfo("total track size: %dkb",disksize/1024);
+#endif
+
+ data=MemMalloc(disksize);
+ memset(data,0,disksize);
+ if (FileReadBinary(edskfilename,(char *)data,disksize)!=disksize) {
+ rasm_printf(ae,KERROR"Cannot read DSK tracks!\n");
+ FreeAssenv(ae);exit(ABORT_ERROR);
+ }
+
+ f=face;
+ for (t=0;t<tracknumber && t<40;t++) {
+ int track_sectorsize;
+
+ curtrack=t*sidenumber+f;
+ i=currenttrackposition;
+ currentsectorposition=i+0x100;
+
+ if (!header[0x34+curtrack] && t<40) {
+ rasm_printf(ae,KERROR"Unexpected unformated track Side %d Track %02d\n",f,t);
+ } else {
+ currenttrackposition+=header[0x34+curtrack]*256;
+
+ if (strncmp((char *)data+i,"Track-Info\r\n",12)) {
+ rasm_printf(ae,KERROR"Invalid track information block side %d track %d\n",f,t);
+ FreeAssenv(ae);exit(ABORT_ERROR);
+ }
+ sectornumber=data[i+21];
+ track_sectorsize=data[i+20];
+ if (sectornumber!=9) {
+ rasm_printf(ae,KERROR"Unsupported track %d (sectornumber=%d sectorsize=%d)\n",t,sectornumber,sectorsize);
+ FreeAssenv(ae);exit(ABORT_ERROR);
+ }
+ memset(checksectorid,0,sizeof(checksectorid));
+ /* we want DATA format */
+ for (s=0;s<sectornumber;s++) {
+ sectorid=data[i+24+8*s+2];
+ if (sectorid>=0xC1 && sectorid<=0xC9) checksectorid[sectorid-0xC1]=1; else {
+ rasm_printf(ae,KERROR"invalid sector id %02X for DATA track %d\n",sectorid,t);
+ return;
+ }
+ sectorsize=data[i+24+8*s+3];
+ if (sectorsize!=2) {
+ rasm_printf(ae,KERROR"invalid sector size track %d\n",t);
+ return;
+ }
+ reallength=data[i+24+8*s+6]+data[i+24+8*s+7]*256; /* real length stored */
+ if (reallength!=512) {
+ rasm_printf(ae,KERROR"invalid sector length %d for track %d\n",reallength,t);
+ return;
+ }
+#if TRACE_EDSK
+ printf("%02X ",sectorid);
+#endif
+ }
+ if (track_sectorsize!=2) {
+ rasm_printf(ae,KWARNING"track %02d has invalid sector size but sectors are OK\n",t);
+ }
+#if TRACE_EDSK
+ printf("\n");
+#endif
+
+ /* piste à piste on lit les blocs DANS L'ORDRE LOGIQUE!!! */
+ for (b=0xC1;b<=0xC9;b++) {
+ tmpcurrentsectorposition=currentsectorposition;
+ for (s=0;s<sectornumber;s++) {
+ if (b==data[i+24+8*s+2]) {
+ memcpy(&curwrap->blocks[curblock][curoffset],&data[tmpcurrentsectorposition],512);
+ curoffset+=512;
+ if (curoffset>=1024) {
+ curoffset=0;
+ curblock++;
+ }
+ }
+ reallength=data[i+24+8*s+6]+data[i+24+8*s+7]*256;
+ tmpcurrentsectorposition+=reallength;
+ }
+ }
+ }
+ }
+
+
+ } else {
+ rasm_printf(ae,KERROR"file [%s] is not a valid (E)DSK floppy image\n",edskfilename);
+ FreeAssenv(ae);exit(-923);
+ }
+ FileReadBinaryClose(edskfilename);
+
+ /* Rasm management of (e)DSK files is AMSDOS compatible, just need to copy CATalog blocks but sort them... */
+ memcpy(&curwrap->entry[0],curwrap->blocks[0],1024);
+ memcpy(&curwrap->entry[32],curwrap->blocks[1],1024);
+ /* tri des entrées selon le user */
+ qsort(curwrap->entry,64,sizeof(struct s_edsk_wrapper_entry),cmpAmsdosentry);
+ curwrap->nbentry=64;
+ for (i=0;i<64;i++) {
+ if (curwrap->entry[i].user==0xE5) {
+ curwrap->nbentry=i;
+ break;
+ }
+ }
+#if TRACE_EDSK
+ printf("%d entr%s found\n",curwrap->nbentry,curwrap->nbentry>1?"ies":"y");
+ for (i=0;i<curwrap->nbentry;i++) {
+ printf("[%02d] - ",i);
+ if (curwrap->entry[i].user<16) {
+ printf("U%02d [%-8.8s.%c%c%c] %c%c subcpt=#%02X rc=#%02X blocks=",curwrap->entry[i].user,curwrap->entry[i].filename,
+ curwrap->entry[i].filename[8]&0x7F,curwrap->entry[i].filename[9]&0x7F,curwrap->entry[i].filename[10],
+ curwrap->entry[i].filename[8]&0x80?'P':'-',curwrap->entry[i].filename[9]&0x80?'H':'-',
+ curwrap->entry[i].subcpt,curwrap->entry[i].rc);
+ for (b=0;b<16;b++) if (curwrap->entry[i].blocks[b]) printf("%s%02X",b>0?" ":"",curwrap->entry[i].blocks[b]); else printf("%s ",b>0?" ":"");
+ if (i&1) printf("\n"); else printf(" | ");
+ } else {
+ printf("free entry = rc= blocks= ");
+ if (i&1) printf("\n"); else printf(" | ");
+ }
+ }
+ if (i&1) printf("\n");
+#endif
+}
+
+struct s_edsk_wrapper *EDSK_select(struct s_assenv *ae,char *edskfilename, int facenumber)
+{
+ #undef FUNC
+ #define FUNC "EDSK_select"
+
+ struct s_edsk_wrapper newwrap={0},*curwrap=NULL;
+ int i;
+#if TRACE_EDSK
+ printf("EDSK_select('%s',%d);\n",edskfilename,facenumber);
+#endif
+ /* check if there is a DSK in memory */
+ for (i=0;i<ae->nbedskwrapper;i++) {
+ if (!strcmp(ae->edsk_wrapper[i].edsk_filename,edskfilename)) {
+#if TRACE_EDSK
+ printf("Found! return %d\n",i);
+#endif
+ return &ae->edsk_wrapper[i];
+ }
+ }
+ /* not in memory, create an empty struct */
+ newwrap.edsk_filename=TxtStrDup(edskfilename);
+ memset(newwrap.entry,0xE5,sizeof(struct s_edsk_wrapper_entry)*64);
+ memset(newwrap.blocks[0],0xE5,1024);
+ memset(newwrap.blocks[1],0xE5,1024);
+#if TRACE_EDSK
+ printf("Not found! create empty struct\n");
+#endif
+ newwrap.face=facenumber;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->edsk_wrapper,&ae->nbedskwrapper,&ae->maxedskwrapper,&newwrap,sizeof(struct s_edsk_wrapper));
+ /* and load files if the DSK exists on disk */
+ curwrap=&ae->edsk_wrapper[ae->nbedskwrapper-1];
+ if (FileExists(edskfilename)) {
+ EDSK_load(ae,curwrap,edskfilename,facenumber);
+ }
+ return curwrap;
+}
+
+int EDSK_addfile(struct s_assenv *ae,char *edskfilename,int facenumber, char *filename,unsigned char *indata,int insize, int offset, int run)
+{
+ #undef FUNC
+ #define FUNC "EDSK_addfile"
+
+ struct s_edsk_wrapper *curwrap=NULL;
+ char amsdos_name[12]={0};
+ int j,i,ia,ib,ie,filesize,idxdata;
+ int fb[180],rc,idxb;
+ unsigned char *data=NULL;
+ int size=0;
+ int firstblock;
+
+ curwrap=EDSK_select(ae,edskfilename,facenumber);
+ /* update struct */
+ size=insize+128;
+ data=MemMalloc(size);
+ strcpy(amsdos_name,MakeAMSDOS_name(ae,filename));
+ memcpy(data,MakeAMSDOSHeader(run,offset,offset+insize,amsdos_name),128);
+ memcpy(data+128,indata,insize);
+ /* overwrite check */
+#if TRACE_EDSK
+ printf("EDSK_addfile will checks %d entr%s for [%s]\n",curwrap->nbentry,curwrap->nbentry>1?"ies":"y",amsdos_name);
+#endif
+ for (i=0;i<curwrap->nbentry;i++) {
+ if (!strncmp((char *)curwrap->entry[i].filename,amsdos_name,11)) {
+ if (!ae->edskoverwrite) {
+ MakeError(ae,NULL,0,"Error - Cannot save [%s] in edsk [%s] with overwrite disabled as the file already exists\n",amsdos_name,edskfilename);
+ MemFree(data);
+ return 0;
+ } else {
+ /* overwriting previous file */
+#if TRACE_EDSK
+ printf(" -> reset previous entry %d with 0xE5\n",i);
+#endif
+ memset(&curwrap->entry[i],0xE5,sizeof(struct s_edsk_wrapper_entry));
+ }
+ }
+ }
+ /* find free blocks */
+#if TRACE_EDSK
+ printf("EDSK_addfile find free blocks\n");
+#endif
+ fb[0]=fb[1]=0;
+ for (i=2;i<180;i++) fb[i]=1;
+ for (i=0;i<curwrap->nbentry;i++) {
+ if (curwrap->entry[i].rc!=0xE5 && curwrap->entry[i].rc!=0) {
+ /* entry found, compute number of blocks to read */
+ rc=curwrap->entry[i].rc/8;
+ if (curwrap->entry[i].rc%8) rc++; /* adjust value */
+ /* mark as used */
+ for (j=0;j<rc;j++) {
+ fb[curwrap->entry[i].blocks[j]]=0;
+ }
+ }
+ }
+ /* set directory, blocks and data in blocks */
+ firstblock=-1;
+ filesize=size;
+ idxdata=0;
+ ia=0;
+
+#if TRACE_EDSK
+ printf("Writing [%s] size=%d\n",amsdos_name,size);
+#endif
+
+ while (filesize>0) {
+ if (filesize>16384) {
+ /* extended entry */
+#if TRACE_EDSK
+ printf("extended entry for file (filesize=%d)\nblocklist: ",filesize);
+#endif
+ if ((ie=EDSK_getdirid(curwrap))==-1) {
+ MakeError(ae,NULL,0,"Error - edsk [%s] DIRECTORY FULL\n",edskfilename);
+ MemFree(data);
+ return 0;
+ }
+ if (curwrap->nbentry<=ie) curwrap->nbentry=ie+1;
+ idxb=0;
+ for (i=0;i<16;i++) {
+ if ((ib=EDSK_getblockid(fb))==-1) {
+ MakeError(ae,NULL,0,"Error - edsk [%s] DISK FULL\n",edskfilename);
+ MemFree(data);
+ return 0;
+ } else {
+ if (firstblock==-1) firstblock=ib;
+
+#if TRACE_EDSK
+ printf("%02X ",ib);
+#endif
+ memcpy(curwrap->blocks[ib],data+idxdata,1024);
+ idxdata+=1024;
+ filesize-=1024;
+ fb[ib]=0;
+ curwrap->entry[ie].blocks[idxb++]=ib;
+ }
+ }
+#if TRACE_EDSK
+ printf("\n");
+#endif
+ memcpy(curwrap->entry[ie].filename,amsdos_name,11);
+ curwrap->entry[ie].subcpt=ia;
+ curwrap->entry[ie].rc=0x80;
+ curwrap->entry[ie].user=0;
+ ia++;
+ idxb=0;
+ } else {
+ /* last entry */
+#if TRACE_EDSK
+ printf("last entry for file (filesize=%d)\nblocklist: ",filesize);
+#endif
+ if ((ie=EDSK_getdirid(curwrap))==-1) {
+ MakeError(ae,NULL,0,"Error - edsk [%s] DIRECTORY FULL\n",edskfilename);
+ MemFree(data);
+ return 0;
+ }
+ if (curwrap->nbentry<=ie) curwrap->nbentry=ie+1;
+ /* calcul du nombre de sous blocs de 128 octets */
+ curwrap->entry[ie].rc=filesize/128;
+ if (filesize%128) {
+ curwrap->entry[ie].rc+=1;
+ }
+ idxb=0;
+ for (i=0;i<16 && filesize>0;i++) {
+ if ((ib=EDSK_getblockid(fb))==-1) {
+ MakeError(ae,NULL,0,"Error - edsk [%s] DISK FULL\n",edskfilename);
+ MemFree(data);
+ return 0;
+ } else {
+ if (firstblock==-1) firstblock=ib;
+#if TRACE_EDSK
+ printf("%02X ",ib);
+#endif
+
+ memcpy(curwrap->blocks[ib],&data[idxdata],filesize>1024?1024:filesize);
+ idxdata+=1024;
+ filesize-=1024;
+ fb[ib]=0;
+ curwrap->entry[ie].blocks[idxb++]=ib;
+ }
+ }
+#if TRACE_EDSK
+ printf("\n");
+#endif
+ filesize=0;
+ memcpy(curwrap->entry[ie].filename,amsdos_name,11);
+ curwrap->entry[ie].subcpt=ia;
+ curwrap->entry[ie].user=0;
+ }
+ }
+
+ MemFree(data);
+ return 1;
+}
+
+void EDSK_build_amsdos_directory(struct s_edsk_wrapper *face)
+{
+ #undef FUNC
+ #define FUNC "EDSK_build_amsdos_directory"
+
+ unsigned char amsdosdir[2048]={0};
+ int i,idx=0,b;
+
+ if (!face) return;
+
+#if TRACE_EDSK
+printf("build amsdos dir with %d entries\n",face->nbentry);
+#endif
+ for (i=0;i<face->nbentry;i++) {
+ if (face->entry[i].rc && face->entry[i].rc!=0xE5) {
+ amsdosdir[idx]=face->entry[i].user;
+ memcpy(amsdosdir+idx+1,face->entry[i].filename,11);
+ amsdosdir[idx+12]=face->entry[i].subcpt;
+ amsdosdir[idx+13]=0;
+ amsdosdir[idx+14]=0;
+ amsdosdir[idx+15]=face->entry[i].rc;
+#if TRACE_EDSK
+printf("%-11.11s [%02X.%02X] blocks:",amsdosdir+idx+1,amsdosdir[idx+12],amsdosdir[idx+15]);
+#endif
+ for (b=0;b<16;b++) {
+ if (face->entry[i].blocks[b]!=0xE5) {
+ amsdosdir[idx+16+b]=face->entry[i].blocks[b];
+#if TRACE_EDSK
+ printf("%s%02X",b>0?".":"",amsdosdir[idx+16+b]);
+#endif
+ } else {
+ amsdosdir[idx+16+b]=0;
+ }
+ }
+#if TRACE_EDSK
+printf("\n");
+#endif
+ }
+ idx+=32;
+ }
+#if TRACE_EDSK
+printf("filling amsdos remaining entries (%d) with #E5\n",64-face->nbentry);
+#endif
+ memset(amsdosdir+idx,0xE5,32*(64-face->nbentry));
+
+ /* AMSDOS directory copy to blocks! */
+ memcpy(face->blocks[0],amsdosdir,1024);
+ memcpy(face->blocks[1],amsdosdir+1024,1024);
+}
+void EDSK_write_file(struct s_assenv *ae,struct s_edsk_wrapper *faceA,struct s_edsk_wrapper *faceB)
+{
+ #undef FUNC
+ #define FUNC "EDSK_write_file"
+
+ struct s_edsk_wrapper emptyface={0};
+ unsigned char header[256]={0};
+ unsigned char trackblock[256]={0};
+ int idblock,blockoffset;
+ int i,t;
+
+ if (!faceA && !faceB) return;
+
+ /* création des deux blocs du directory par face */
+ EDSK_build_amsdos_directory(faceA);
+ EDSK_build_amsdos_directory(faceB);
+ /* écriture header */
+ strcpy((char *)header,"EXTENDED CPC DSK File\r\nDisk-Info\r\n");
+ strcpy((char *)header+0x22,RASM_SNAP_VERSION);
+ header[0x30]=40;
+ if (!faceA) {
+ faceA=&emptyface;
+ faceA->edsk_filename=TxtStrDup(faceB->edsk_filename);
+ }
+#if TRACE_EDSK
+ printf("deleting [%s]\n",faceA->edsk_filename);
+#endif
+ FileRemoveIfExists(faceA->edsk_filename);
+
+ if (faceB!=NULL) header[0x31]=2; else header[0x31]=1;
+ for (i=0;i<header[0x30]*header[0x31];i++) header[0x34+i]=19; /* tracksize=(9*512+256)/256 */
+#if TRACE_EDSK
+ printf("writing EDSK header (256b)\n");
+#endif
+ FileWriteBinary(faceA->edsk_filename,(char *)header,256);
+
+ /* écriture des pistes */
+ for (t=0;t<40;t++) {
+ strcpy((char *)trackblock,"Track-Info\r\n");
+ trackblock[0x10]=t;
+ trackblock[0x11]=0;
+ trackblock[0x14]=2;
+ trackblock[0x15]=9;
+ trackblock[0x16]=0x4E;
+ trackblock[0x17]=0xE5;
+ i=0;
+ while (1) {
+ trackblock[0x18+i*8+0]=trackblock[0x10];
+ trackblock[0x18+i*8+1]=trackblock[0x11];
+ trackblock[0x18+i*8+2]=(i>>1)+0xC1;
+#if TRACE_EDSK
+ if (t<3) printf("%02X ",trackblock[0x18+i*8+2]);
+#endif
+ trackblock[0x18+i*8+3]=2;
+ trackblock[0x18+i*8+4]=0;
+ trackblock[0x18+i*8+5]=0;
+ trackblock[0x18+i*8+6]=0;
+ trackblock[0x18+i*8+7]=2;
+ i++;
+ if (i==9) break;
+ /* interleave */
+ trackblock[0x18+i*8+0]=trackblock[0x10];
+ trackblock[0x18+i*8+1]=trackblock[0x11];
+ trackblock[0x18+i*8+2]=(i>>1)+0xC6; /* start at C6 */
+#if TRACE_EDSK
+ if (t<3) printf("%02X ",trackblock[0x18+i*8+2]);
+#endif
+ trackblock[0x18+i*8+3]=2;
+ trackblock[0x18+i*8+4]=0;
+ trackblock[0x18+i*8+5]=0;
+ trackblock[0x18+i*8+6]=0;
+ trackblock[0x18+i*8+7]=2;
+ i++;
+ }
+#if TRACE_EDSK
+ if (t<3) printf("\n"); else if (t==3) printf("...\n");
+#endif
+ /* écriture du track info */
+ FileWriteBinary(faceA->edsk_filename,(char *)trackblock,256);
+
+
+ /* il faut convertir les blocs logiques en secteurs physiques ET entrelacés */
+ idblock=t*9/2;
+ blockoffset=((t*9)%2)*512;
+
+ /* le premier secteur de la piste est à cheval sur le bloc logique une fois sur deux */
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock][0]+blockoffset,512); /* C1 */
+ if (!blockoffset) {
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+2][0]+512,512); /* C6 */
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+0][0]+512,512); /* C2 */
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+3][0]+0,512); /* C7 */
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+1][0]+0,512); /* C3 */
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+3][0]+512,512); /* C8 */
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+1][0]+512,512); /* C4 */
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+4][0]+0,512); /* C9 */
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+2][0]+0,512); /* C5 */
+ } else {
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+3][0]+0,512); /* C6 */
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+1][0]+0,512); /* C2 */
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+3][0]+512,512); /* C7 */
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+1][0]+512,512); /* C3 */
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+4][0]+0,512); /* C8 */
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+2][0]+0,512); /* C4 */
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+4][0]+512,512); /* C9 */
+ FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+2][0]+512,512); /* C5 */
+ }
+
+ /* @@TODO ça semble un peu foireux comme procédé */
+ if (faceB) {
+#if TRACE_EDSK
+ printf("writing EDSK face B /!\\ probably NOT WORKING !!!\n");
+#endif
+ trackblock[0x11]=1;
+ for (i=0;i<9;i++) {
+ trackblock[0x18+i*8+0]=trackblock[0x10];
+ trackblock[0x18+i*8+1]=trackblock[0x11];
+ }
+ /* écriture du track info */
+ FileWriteBinary(faceB->edsk_filename,(char *)trackblock,256);
+ /* écriture des secteurs */
+ idblock=t*9/2;
+ blockoffset=((t*9)%2)*512;
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock][0]+blockoffset,512);
+ if (!blockoffset) {
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+2][0]+512,512); /* C6 */
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+0][0]+512,512); /* C2 */
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+3][0]+0,512); /* C7 */
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+1][0]+0,512); /* C3 */
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+3][0]+512,512); /* C8 */
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+1][0]+512,512); /* C4 */
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+4][0]+0,512); /* C9 */
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+2][0]+0,512); /* C5 */
+ } else {
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+3][0]+0,512); /* C6 */
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+1][0]+0,512); /* C2 */
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+3][0]+512,512); /* C7 */
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+1][0]+512,512); /* C3 */
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+4][0]+0,512); /* C8 */
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+2][0]+0,512); /* C4 */
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+4][0]+512,512); /* C9 */
+ FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+2][0]+512,512); /* C5 */
+ }
+ }
+ }
+ FileWriteBinaryClose(faceA->edsk_filename);
+ rasm_printf(ae,KIO"Write edsk file %s\n",faceA->edsk_filename);
+}
+void EDSK_write(struct s_assenv *ae)
+{
+ #undef FUNC
+ #define FUNC "EDSK_write"
+
+ struct s_edsk_wrapper *faceA,*faceB;
+ char *edskfilename;
+ int i,j;
+
+ /* on passe en revue toutes les structs */
+ for (i=0;i<ae->nbedskwrapper;i++) {
+ /* already done */
+ if (ae->edsk_wrapper[i].face==-1) continue;
+
+ switch (ae->edsk_wrapper[i].face) {
+ default:
+ case 0:faceA=&ae->edsk_wrapper[i];faceB=NULL;break;
+ case 1:faceA=NULL;faceB=&ae->edsk_wrapper[i];break;
+ }
+ /* doit-on fusionner avec une autre face? */
+ for (j=i+1;j<ae->nbedskwrapper;j++) {
+ if (!strcmp(ae->edsk_wrapper[i].edsk_filename,ae->edsk_wrapper[j].edsk_filename)) {
+ /* found another face for the floppy */
+ switch (ae->edsk_wrapper[j].face) {
+ default:
+ case 0:faceA=&ae->edsk_wrapper[j];break;
+ case 1:faceB=&ae->edsk_wrapper[j];break;
+ }
+ }
+ }
+ EDSK_write_file(ae,faceA,faceB);
+ }
+}
+void PopAllSave(struct s_assenv *ae)
+{
+ #undef FUNC
+ #define FUNC "PopAllSave"
+
+ unsigned char *AmsdosHeader;
+ char *dskfilename;
+ char *filename;
+ int offset,size,run;
+ int i,is,erreur=0,touched;
+
+ for (is=0;is<ae->nbsave;is++) {
+ /* avoid quotes */
+ filename=ae->wl[ae->save[is].iw].w;
+ filename[strlen(filename)-1]=0;
+ filename=TxtStrDup(filename+1);
+ /* translate tags! */
+ filename=TranslateTag(ae,filename,&touched,1,E_TAGOPTION_REMOVESPACE);
+
+#if TRACE_EDSK
+ printf("woff=[%s](%d) wsize=[%s](%d)\n",ae->wl[ae->save[is].ioffset].w,ae->save[is].ioffset,ae->wl[ae->save[is].isize].w,ae->save[is].isize);
+#endif
+
+ ae->idx=ae->save[is].ioffset; /* exp hack */
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0);
+ offset=RoundComputeExpression(ae,ae->wl[ae->idx].w,0,0,0);
+
+ ae->idx=ae->save[is].isize; /* exp hack */
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0);
+ size=RoundComputeExpression(ae,ae->wl[ae->idx].w,0,0,0);
+
+ ae->idx=ae->save[is].irun; /* exp hack */
+ if (ae->idx) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0);
+ run=RoundComputeExpression(ae,ae->wl[ae->idx].w,0,0,0);
+ } else {
+ run=offset;
+ }
+
+ if (size<1 || size>65536) {
+ MakeError(ae,NULL,0,"cannot save [%s] as the size is invalid!\n",filename);
+ continue;
+ }
+ if (offset<0 || offset>65535) {
+ MakeError(ae,NULL,0,"cannot save [%s] as the offset is invalid!\n",filename);
+ continue;
+ }
+ if (offset+size>65536) {
+ MakeError(ae,NULL,0,"cannot save [%s] as the offset+size will be out of bounds!\n",filename);
+ continue;
+ }
+ /* DSK management */
+ if (ae->save[is].dsk) {
+ if (ae->save[is].iwdskname!=-1) {
+ /* obligé de dupliquer à cause du reuse */
+ dskfilename=TxtStrDup(ae->wl[ae->save[is].iwdskname].w);
+ dskfilename[strlen(dskfilename)-1]=0;
+ if (!EDSK_addfile(ae,dskfilename+1,ae->save[is].face,filename,ae->mem[ae->save[is].ibank]+offset,size,offset,run)) {
+ erreur++;
+ break;
+ }
+ MemFree(dskfilename);
+ }
+ } else if (ae->save[is].tape) {
+ char TZX_header[10];
+ unsigned char IDval[2];
+ int wrksize,nbblock;
+
+ /* output file on filesystem */
+ FileRemoveIfExists(filename);
+
+ strcpy(TZX_header,"ZXTape!");
+ TZX_header[7]=0x1A;
+ TZX_header[8]=1;
+ TZX_header[9]=20;
+ FileWriteBinary(filename,(char *)TZX_header,10);
+
+ IDval[0]=0x20;
+ FileWriteBinary(filename,(char *)IDval,1);
+ IDval[0]=0x03;
+ IDval[1]=0x03;
+ FileWriteBinary(filename,(char *)IDval,2); // first silence
+
+ IDval[0]=0x10;
+ FileWriteBinary(filename,(char *)IDval,1);
+ IDval[0]=0x03;
+ IDval[1]=0x03;
+ FileWriteBinary(filename,(char *)IDval,2); // little silence
+ if (size+128<=2048) wrksize=size+128; else wrksize=2048;
+ IDval[0]=(wrksize+128) & 0xFF;
+ IDval[1]=((wrksize+128)>>8) & 0xFF;
+ FileWriteBinary(filename,(char *)IDval,2); // block len
+ nbblock=1;
+ AmsdosHeader=MakeAMSDOSHeader(run,offset,offset+size,MakeAMSDOS_name(ae,filename));
+ FileWriteBinary(filename,(char *)AmsdosHeader,128);
+ if (size<=2048-128) {
+ FileWriteBinary(filename,(char*)ae->mem[ae->save[is].ibank]+offset,size);
+ } else {
+ FileWriteBinary(filename,(char*)ae->mem[ae->save[is].ibank]+offset,2048-128);
+ size=size-2048+128;
+ while (size>0) {
+ nbblock++;
+ /* additionnal block */
+ IDval[0]=0x10;
+ FileWriteBinary(filename,(char *)IDval,1);
+ IDval[0]=0x04;
+ IDval[1]=0x04;
+ FileWriteBinary(filename,(char *)IDval,2); // silence 1s delay
+ if (size<=2048) wrksize=size; else wrksize=2048;
+ IDval[0]=(wrksize+128) & 0xFF;
+ IDval[1]=((wrksize+128)>>8) & 0xFF;
+ FileWriteBinary(filename,(char *)IDval,2); // block len
+ FileWriteBinary(filename,(char*)ae->mem[ae->save[is].ibank]+offset,wrksize);
+ /* adjust */
+ size=size-2048;
+ }
+ }
+ FileWriteBinaryClose(filename);
+ rasm_printf(ae,KIO"Write tape file %s (%d block%s)\n",filename,nbblock,nbblock>1?"s":"");
+ } else {
+ /* output file on filesystem */
+ rasm_printf(ae,KIO"Write binary file %s (%d byte%s)\n",filename,size,size>1?"s":"");
+ FileRemoveIfExists(filename);
+ if (ae->save[is].amsdos) {
+ AmsdosHeader=MakeAMSDOSHeader(run,offset,offset+size,MakeAMSDOS_name(ae,filename));
+ FileWriteBinary(filename,(char *)AmsdosHeader,128);
+ }
+ FileWriteBinary(filename,(char*)ae->mem[ae->save[is].ibank]+offset,size);
+ FileWriteBinaryClose(filename);
+ }
+ MemFree(filename);
+ }
+ if (!erreur) EDSK_write(ae);
+
+ for (i=0;i<ae->nbedskwrapper;i++) {
+ MemFree(ae->edsk_wrapper[i].edsk_filename);
+ }
+ if (ae->maxedskwrapper) MemFree(ae->edsk_wrapper);
+
+ if (ae->nbsave) {
+ MemFree(ae->save);
+ }
+}
+
+void PopAllExpression(struct s_assenv *ae, int crunched_zone)
+{
+ #undef FUNC
+ #define FUNC "PopAllExpression"
+
+ static int first=1;
+ double v;
+ long r;
+ int i;
+ unsigned char *mem;
+ char *expr;
+
+ /* pop all expressions BUT thoses who where already computed (in crunched blocks) */
+
+ /* calcul des labels et expressions en zone crunch (et locale?)
+ les labels doivent pointer:
+ - une valeur absolue (numerique ou variable calculee) -> completement transparent
+ - un label dans la meme zone de crunch -> label->lz=1 && verif de la zone crunch
+ - un label hors zone crunch MAIS avant toute zone de crunch de la bank destination (!label->lz)
+
+ idealement on doit tolerer les adresses situees apres le crunch dans une autre ORG zone!
+
+ on utilise ae->stage pour créer un état intermédiaire dans le ComputeExpressionCore
+ */
+ if (crunched_zone>=0) {
+ ae->stage=1;
+ } else {
+ /* on rescanne tout pour combler les trous */
+ ae->stage=2;
+ first=1;
+ }
+
+ for (i=first;i<ae->ie;i++) {
+ /* first compute only crunched expression (0,1,2,3,...) then (-1) at the end */
+ if (crunched_zone>=0) {
+ /* calcul des expressions en zone crunch */
+ if (ae->expression[i].lz<crunched_zone) continue;
+ if (ae->expression[i].lz>crunched_zone) {
+ first=i;
+ break;
+ }
+ } else {
+ if (ae->expression[i].lz>=0) continue;
+ }
+
+ mem=ae->mem[ae->expression[i].ibank];
+
+ if (ae->expression[i].reference) {
+ expr=ae->expression[i].reference;
+ } else {
+ expr=ae->wl[ae->expression[i].iw].w;
+ }
+ v=ComputeExpressionCore(ae,expr,ae->expression[i].ptr,i);
+ r=(long)floor(v+ae->rough);
+ switch (ae->expression[i].zetype) {
+ case E_EXPRESSION_J8:
+ r=r-ae->expression[i].ptr-2;
+ if (r<-128 || r>127) {
+ MakeError(ae,GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,"relative offset %d too far [%s]\n",r,ae->wl[ae->expression[i].iw].w);
+ }
+ mem[ae->expression[i].wptr]=(unsigned char)r;
+ break;
+ case E_EXPRESSION_IV81:
+ /* for enhanced 16bits instructions */
+ r++;
+ case E_EXPRESSION_0V8:
+ case E_EXPRESSION_IV8:
+ case E_EXPRESSION_3V8:
+ case E_EXPRESSION_V8:
+ if (r>255 || r<-128) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: truncating value #%X to #%X\n",GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,r,r&0xFF);
+ }
+ mem[ae->expression[i].wptr]=(unsigned char)r;
+ break;
+ case E_EXPRESSION_IV16:
+ case E_EXPRESSION_V16:
+ case E_EXPRESSION_V16C:
+ case E_EXPRESSION_0V16:
+ if (r>65535 || r<-32768) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: truncating value #%X to #%X\n",GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,r,r&0xFFFF);
+ }
+ mem[ae->expression[i].wptr]=(unsigned char)r&0xFF;
+ mem[ae->expression[i].wptr+1]=(unsigned char)((r&0xFF00)>>8);
+ break;
+ case E_EXPRESSION_0V32:
+ /* meaningless in 32 bits architecture... */
+ if (v>4294967295 || v<-2147483648) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: truncating value\n",GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l);
+ }
+ mem[ae->expression[i].wptr]=(unsigned char)r&0xFF;
+ mem[ae->expression[i].wptr+1]=(unsigned char)((r>>8)&0xFF);
+ mem[ae->expression[i].wptr+2]=(unsigned char)((r>>16)&0xFF);
+ mem[ae->expression[i].wptr+3]=(unsigned char)((r>>24)&0xFF);
+ break;
+ case E_EXPRESSION_0VR:
+ /* convert v double value to Amstrad REAL */
+ memcpy(&mem[ae->expression[i].wptr],__internal_MakeAmsdosREAL(ae,v,i),5);
+ break;
+ case E_EXPRESSION_IM:
+ switch (r) {
+ case 0x00:mem[ae->expression[i].wptr]=0x46;break;
+ case 0x01:mem[ae->expression[i].wptr]=0x56;break;
+ case 0x02:mem[ae->expression[i].wptr]=0x5E;break;
+ default:
+ MakeError(ae,GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,"IM 0,1 or 2 only\n");
+ mem[ae->expression[i].wptr]=0;
+ }
+ break;
+ case E_EXPRESSION_RST:
+ switch (r) {
+ case 0x00:mem[ae->expression[i].wptr]=0xC7;break;
+ case 0x08:mem[ae->expression[i].wptr]=0xCF;break;
+ case 0x10:mem[ae->expression[i].wptr]=0xD7;break;
+ case 0x18:mem[ae->expression[i].wptr]=0xDF;break;
+ case 0x20:mem[ae->expression[i].wptr]=0xE7;break;
+ case 0x28:mem[ae->expression[i].wptr]=0xEF;break;
+ case 0x30:mem[ae->expression[i].wptr]=0xF7;break;
+ case 0x38:mem[ae->expression[i].wptr]=0xFF;break;
+ default:
+ MakeError(ae,GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,"RST #0,#8,#10,#18,#20,#28,#30,#38 only\n");
+ mem[ae->expression[i].wptr]=0;
+ }
+ break;
+ case E_EXPRESSION_RUN:
+ if (r<0 || r>65535) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: run adress truncated from %X to %X\n",GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,r,r&0xFFFF);
+ }
+ ae->snapshot.registers.LPC=r&0xFF;
+ ae->snapshot.registers.HPC=(r>>8)&0xFF;
+ break;
+ case E_EXPRESSION_ZXRUN:
+ if (r<0 || r>65535) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: run adress truncated from %X to %X\n",GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,r,r&0xFFFF);
+ }
+ ae->zxsnapshot.run=r&0xFFFF;
+ break;
+ case E_EXPRESSION_ZXSTACK:
+ if (r<0 || r>65535) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: stack adress truncated from %X to %X\n",GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,r,r&0xFFFF);
+ }
+ ae->zxsnapshot.stack=r&0xFFFF;
+ break;
+ case E_EXPRESSION_BRS:
+ if (r>=0 && r<8) {
+ mem[ae->expression[i].wptr]+=r*8;
+ } else {
+ MakeError(ae,GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,"SET,RES,BIT shift value from 0 to 7 only\n");
+ }
+ break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"FATAL - unknown expression type\n",GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l);
+ FreeAssenv(ae);exit(-8);
+ }
+ }
+}
+
+void InsertLabelToTree(struct s_assenv *ae, struct s_label *label)
+{
+ #undef FUNC
+ #define FUNC "InsertLabelToTree"
+
+ struct s_crclabel_tree *curlabeltree;
+ int radix,dek=32;
+
+ curlabeltree=&ae->labeltree;
+ while (dek) {
+ dek=dek-8;
+ radix=(label->crc>>dek)&0xFF;
+ if (curlabeltree->radix[radix]) {
+ curlabeltree=curlabeltree->radix[radix];
+ } else {
+ curlabeltree->radix[radix]=MemMalloc(sizeof(struct s_crclabel_tree));
+ curlabeltree=curlabeltree->radix[radix];
+ memset(curlabeltree,0,sizeof(struct s_crclabel_tree));
+ }
+ }
+ ObjectArrayAddDynamicValueConcat((void**)&curlabeltree->label,&curlabeltree->nlabel,&curlabeltree->mlabel,&label[0],sizeof(struct s_label));
+}
+
+/* use by structure mechanism and label import to add fake labels */
+void PushLabelLight(struct s_assenv *ae, struct s_label *curlabel) {
+ #undef FUNC
+ #define FUNC "PushLabelLight"
+
+ struct s_label *searched_label;
+
+ /* PushLabel light */
+ if ((searched_label=SearchLabel(ae,curlabel->name,curlabel->crc))!=NULL) {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"%s caused duplicate label [%s]\n",ae->idx?"Structure insertion":"Label import",curlabel->name);
+ MemFree(curlabel->name);
+ } else {
+ curlabel->backidx=ae->il;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->label,&ae->il,&ae->ml,curlabel,sizeof(struct s_label));
+ InsertLabelToTree(ae,curlabel);
+ }
+}
+void PushLabel(struct s_assenv *ae)
+{
+ #undef FUNC
+ #define FUNC "PushLabel"
+
+ struct s_label curlabel={0},*searched_label;
+ char *curlabelname;
+ int i;
+ /* label with counters */
+ struct s_expr_dico *curdic;
+ char curval[32];
+ char *varbuffer,*expr;
+ char *starttag,*endtag,*tagcheck;
+ int taglen,tagidx,lenw,tagcount=0;
+ int crc,newlen,touched;
+
+ if (ae->AutomateValidLabelFirst[ae->wl[ae->idx].w[0]]) {
+ for (i=1;ae->wl[ae->idx].w[i];i++) {
+ if (ae->wl[ae->idx].w[i]=='{') tagcount++; else if (ae->wl[ae->idx].w[i]=='}') tagcount--;
+ if (!tagcount) {
+ if (!ae->AutomateValidLabel[ae->wl[ae->idx].w[i]]) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid char in label declaration (%c)\n",ae->wl[ae->idx].w[i]);
+ return;
+ }
+ }
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid first char in label declaration (%c)\n",ae->wl[ae->idx].w[0]);
+ return;
+ }
+
+ switch (i) {
+ case 1:
+ switch (ae->wl[ae->idx].w[0]) {
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'H':
+ case 'L':
+ case 'I':
+ case 'R':
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot use reserved word [%s] for label\n",ae->wl[ae->idx].w);
+ return;
+ default:break;
+ }
+ break;
+ case 2:
+ if (strcmp(ae->wl[ae->idx].w,"AF")==0 || strcmp(ae->wl[ae->idx].w,"BC")==0 || strcmp(ae->wl[ae->idx].w,"DE")==0 || strcmp(ae->wl[ae->idx].w,"HL")==0 ||
+ strcmp(ae->wl[ae->idx].w,"IX")==0 || strcmp(ae->wl[ae->idx].w,"IY")==0 || strcmp(ae->wl[ae->idx].w,"SP")==0 ||
+ strcmp(ae->wl[ae->idx].w,"LX")==0 || strcmp(ae->wl[ae->idx].w,"HX")==0 || strcmp(ae->wl[ae->idx].w,"XL")==0 || strcmp(ae->wl[ae->idx].w,"XH")==0 ||
+ strcmp(ae->wl[ae->idx].w,"LY")==0 || strcmp(ae->wl[ae->idx].w,"HY")==0 || strcmp(ae->wl[ae->idx].w,"YL")==0 || strcmp(ae->wl[ae->idx].w,"YH")==0) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot use reserved word [%s] for label\n",ae->wl[ae->idx].w);
+ return;
+ }
+ break;
+ case 3:
+ if (strcmp(ae->wl[ae->idx].w,"IXL")==0 || strcmp(ae->wl[ae->idx].w,"IYL")==0 || strcmp(ae->wl[ae->idx].w,"IXH")==0 || strcmp(ae->wl[ae->idx].w,"IYH")==0) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot use reserved word [%s] for label\n",ae->wl[ae->idx].w);
+ return;
+ }
+ break;
+ case 4:
+ if (strcmp(ae->wl[ae->idx].w,"VOID")==0) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot use reserved word [%s] for label\n",ae->wl[ae->idx].w);
+ return;
+ }
+ default:break;
+ }
+
+ /*******************************************************
+ v a r i a b l e s i n l a b e l n a m e
+ *******************************************************/
+ varbuffer=TranslateTag(ae,TxtStrDup(ae->wl[ae->idx].w),&touched,1,E_TAGOPTION_NONE);
+
+ /**************************************************
+ s t r u c t u r e d e c l a r a t i o n
+ **************************************************/
+ if (ae->getstruct) {
+ struct s_rasmstructfield rasmstructfield={0};
+ if (varbuffer[0]=='@') {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Please no local label in a struct [%s]\n",ae->wl[ae->idx].w);
+ return;
+ }
+ /* copy label+offset in the structure */
+ rasmstructfield.name=TxtStrDup(varbuffer);
+ rasmstructfield.offset=ae->codeadr;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->rasmstruct[ae->irasmstruct-1].rasmstructfield,
+ &ae->rasmstruct[ae->irasmstruct-1].irasmstructfield,&ae->rasmstruct[ae->irasmstruct-1].mrasmstructfield,
+ &rasmstructfield,sizeof(rasmstructfield));
+ /* label is structname+field */
+ curlabelname=curlabel.name=MemMalloc(strlen(ae->rasmstruct[ae->irasmstruct-1].name)+strlen(varbuffer)+2);
+ sprintf(curlabel.name,"%s.%s",ae->rasmstruct[ae->irasmstruct-1].name,varbuffer);
+ curlabel.iw=-1;
+ /* legacy */
+ curlabel.crc=GetCRC(curlabel.name);
+ curlabel.ptr=ae->codeadr;
+#if TRACE_STRUCT
+ printf("pushLabel (struct) [%X] [%s]\n",curlabel.ptr,curlabel.name);
+#endif
+ } else {
+ /**************************************************
+ l a b e l s
+ **************************************************/
+ /* labels locaux */
+ if (varbuffer[0]=='@' && (ae->ir || ae->iw || ae->imacro)) {
+ curlabel.iw=-1;
+ curlabel.local=1;
+ curlabelname=curlabel.name=MakeLocalLabel(ae,varbuffer,NULL);
+ curlabel.crc=GetCRC(curlabel.name);
+
+ /* local labels ALSO set new reference */
+ if (ae->lastglobalalloc) {
+//printf("push LOCAL is freeing lastgloballabel\n");
+ MemFree(ae->lastgloballabel);
+ }
+ ae->lastgloballabel=TxtStrDup(curlabelname);
+//printf("push LOCAL as reference for proximity label -> [%s]\n",ae->lastgloballabel);
+ ae->lastgloballabellen=strlen(ae->lastgloballabel);
+ ae->lastglobalalloc=1;
+ } else {
+ switch (varbuffer[0]) {
+ case '.':
+ if (ae->dams) {
+ /* old Dams style declaration (remove the dot) */
+ i=0;
+ do {
+ varbuffer[i]=varbuffer[i+1];
+ ae->wl[ae->idx].w[i]=ae->wl[ae->idx].w[i+1];
+ i++;
+ } while (varbuffer[i]!=0);
+ if (!touched) {
+ curlabel.iw=ae->idx;
+ } else {
+ curlabel.iw=-1;
+ curlabel.name=varbuffer;
+ }
+ curlabel.crc=GetCRC(varbuffer);
+ curlabelname=varbuffer;
+ } else {
+ /* proximity labels */
+ if (ae->lastgloballabel) {
+ curlabelname=MemMalloc(strlen(varbuffer)+1+ae->lastgloballabellen);
+ sprintf(curlabelname,"%s%s",ae->lastgloballabel,varbuffer);
+ MemFree(varbuffer);
+ touched=1; // cause realloc!
+ curlabel.iw=-1;
+ curlabel.name=varbuffer=curlabelname;
+ curlabel.crc=GetCRC(varbuffer);
+//printf("push proximity label that may be exported [%s]->[%s]\n",ae->wl[ae->idx].w,varbuffer);
+ } else {
+ /* MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"cannot create proximity label [%s] as there is no previous global label\n",varbuffer);
+ return; */
+
+ // not optimal but!
+ curlabelname=TxtStrDup(varbuffer);
+ MemFree(varbuffer);
+ touched=1; // cause realloc!
+ curlabel.iw=-1;
+ curlabel.name=varbuffer=curlabelname;
+ curlabel.crc=GetCRC(varbuffer);
+ }
+ }
+ break;
+ default:
+ if (!touched) {
+ curlabel.iw=ae->idx;
+ } else {
+ curlabel.iw=-1;
+ curlabel.name=varbuffer;
+ }
+ curlabel.crc=GetCRC(varbuffer);
+ curlabelname=varbuffer;
+ /* global labels set new reference */
+ if (ae->lastglobalalloc) MemFree(ae->lastgloballabel);
+ ae->lastgloballabel=ae->wl[ae->idx].w;
+ ae->lastsuperglobal=ae->wl[ae->idx].w;
+ ae->lastgloballabellen=strlen(ae->wl[ae->idx].w);
+ ae->lastglobalalloc=0;
+//printf("SET global label [%s] l=%d\n",ae->lastgloballabel,ae->lastgloballabellen);
+ break;
+ }
+
+ /* contrôle dico uniquement avec des labels non locaux */
+ if (SearchDico(ae,curlabelname,curlabel.crc)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"cannot create label [%s] as there is already a variable with the same name\n",curlabelname);
+ return;
+ }
+ if(SearchAlias(ae,curlabel.crc,curlabelname)!=-1) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"cannot create label [%s] as there is already an alias with the same name\n",curlabelname);
+ return;
+ }
+ }
+ curlabel.ptr=ae->codeadr;
+ curlabel.ibank=ae->activebank;
+ curlabel.iorgzone=ae->io-1;
+ curlabel.lz=ae->lz;
+ }
+
+ if ((searched_label=SearchLabel(ae,curlabelname,curlabel.crc))!=NULL) {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"Duplicate label [%s] - previously defined in [%s:%d]\n",curlabelname,ae->filename[searched_label->fileidx],searched_label->fileline);
+ if (curlabel.iw==-1) MemFree(curlabelname);
+ } else {
+//printf("PushLabel(%s) name=%s crc=%X\n",curlabelname,curlabel.name?curlabel.name:"null",curlabel.crc);
+ curlabel.fileidx=ae->wl[ae->idx].ifile;
+ curlabel.fileline=ae->wl[ae->idx].l;
+ curlabel.autorise_export=ae->autorise_export;
+ curlabel.backidx=ae->il;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->label,&ae->il,&ae->ml,&curlabel,sizeof(curlabel));
+ InsertLabelToTree(ae,&curlabel);
+ }
+
+ if (!touched) MemFree(varbuffer);
+}
+
+
+unsigned char *EncodeSnapshotRLE(unsigned char *memin, int *lenout) {
+ #undef FUNC
+ #define FUNC "EncodeSnapshotRLE"
+
+ int i,cpt,idx=0;
+ unsigned char *memout=NULL;
+
+ memout=MemMalloc(65540);
+
+ for (i=0;i<65536;) {
+ for (cpt=1;cpt<255;cpt++) if (memin[i]!=memin[i+cpt]) break;
+ if (cpt>=3 || memin[i]==0xE5) {
+ memout[idx++]=0xE5;
+ memout[idx++]=cpt;
+ memout[idx++]=memin[i];
+ i+=cpt;
+ } else {
+ memout[idx++]=memin[i++];
+ }
+ }
+ if (lenout) *lenout=idx;
+ if (idx<65536) return memout;
+
+ MemFree(memout);
+ return NULL;
+}
+
+
+
+#undef FUNC
+#define FUNC "Instruction CORE"
+
+void _IN(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) {
+ if (strcmp(ae->wl[ae->idx+2].w,"(C)")==0) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_0:
+ case CRC_F:___output(ae,0xED);___output(ae,0x70);ae->nop+=4;break;
+ case CRC_A:___output(ae,0xED);___output(ae,0x78);ae->nop+=3;break;
+ case CRC_B:___output(ae,0xED);___output(ae,0x40);ae->nop+=4;break;
+ case CRC_C:___output(ae,0xED);___output(ae,0x48);ae->nop+=4;break;
+ case CRC_D:___output(ae,0xED);___output(ae,0x50);ae->nop+=4;break;
+ case CRC_E:___output(ae,0xED);___output(ae,0x58);ae->nop+=4;break;
+ case CRC_H:___output(ae,0xED);___output(ae,0x60);ae->nop+=4;break;
+ case CRC_L:___output(ae,0xED);___output(ae,0x68);ae->nop+=4;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is IN [0,F,A,B,C,D,E,H,L],(C)\n");
+ }
+ } else if (strcmp(ae->wl[ae->idx+1].w,"A")==0 && StringIsMem(ae->wl[ae->idx+2].w)) {
+ ___output(ae,0xDB);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V8);
+ ae->nop+=3;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IN [0,F,A,B,C,D,E,H,L],(C) or IN A,(n) only\n");
+ }
+ ae->idx+=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IN [0,F,A,B,C,D,E,H,L],(C) or IN A,(n) only\n");
+ }
+}
+
+void _OUT(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) {
+ if (strcmp(ae->wl[ae->idx+1].w,"(C)")==0) {
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_0:___output(ae,0xED);___output(ae,0x71);ae->nop+=4;break;
+ case CRC_A:___output(ae,0xED);___output(ae,0x79);ae->nop+=4;break;
+ case CRC_B:___output(ae,0xED);___output(ae,0x41);ae->nop+=4;break;
+ case CRC_C:___output(ae,0xED);___output(ae,0x49);ae->nop+=4;break;
+ case CRC_D:___output(ae,0xED);___output(ae,0x51);ae->nop+=4;break;
+ case CRC_E:___output(ae,0xED);___output(ae,0x59);ae->nop+=4;break;
+ case CRC_H:___output(ae,0xED);___output(ae,0x61);ae->nop+=4;break;
+ case CRC_L:___output(ae,0xED);___output(ae,0x69);ae->nop+=4;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is OUT (C),[0,A,B,C,D,E,H,L]\n");
+ }
+ } else if (strcmp(ae->wl[ae->idx+2].w,"A")==0 && StringIsMem(ae->wl[ae->idx+1].w)) {
+ ___output(ae,0xD3);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_V8);
+ ae->nop+=3;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"OUT (C),[0,A,B,C,D,E,H,L] or OUT (n),A only\n");
+ }
+ ae->idx+=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"OUT (C),[0,A,B,C,D,E,H,L] or OUT (n),A only\n");
+ }
+}
+
+void _EX(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_HL:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_DE:___output(ae,0xEB);ae->nop+=1;break;
+ case CRC_MSP:___output(ae,0xE3);ae->nop+=6;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is EX HL,[(SP),DE]\n");
+ }
+ break;
+ case CRC_AF:
+ if (strcmp(ae->wl[ae->idx+2].w,"AF'")==0) {
+ ___output(ae,0x08);ae->nop+=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is EX AF,AF'\n");
+ }
+ break;
+ case CRC_MSP:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_HL:___output(ae,0xE3);ae->nop+=6;break;
+ case CRC_IX:___output(ae,0xDD);___output(ae,0xE3);ae->nop+=7;break;
+ case CRC_IY:___output(ae,0xFD);___output(ae,0xE3);ae->nop+=7;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is EX (SP),[HL,IX,IY]\n");
+ }
+ break;
+ case CRC_DE:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_HL:___output(ae,0xEB);ae->nop+=1;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is EX DE,HL\n");
+ }
+ break;
+ case CRC_IX:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_MSP:___output(ae,0xDD);___output(ae,0xE3);ae->nop+=7;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is EX IX,(SP)\n");
+ }
+ break;
+ case CRC_IY:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_MSP:___output(ae,0xFD);___output(ae,0xE3);ae->nop+=7;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is EX IY,(SP)\n");
+ }
+ break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is EX [AF,DE,HL,(SP),IX,IY],reg16\n");
+ }
+ ae->idx+=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use EX reg16,reg16\n");
+ }
+}
+
+void _SBC(struct s_assenv *ae) {
+ if ((!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) || ((!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) && strcmp(ae->wl[ae->idx+1].w,"A")==0)) {
+ if (!ae->wl[ae->idx+1].t) ae->idx++;
+ /* do implicit A */
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_A:___output(ae,0x9F);ae->nop+=1;break;
+ case CRC_MHL:___output(ae,0x9E);ae->nop+=2;break;
+ case CRC_B:___output(ae,0x98);ae->nop+=1;break;
+ case CRC_C:___output(ae,0x99);ae->nop+=1;break;
+ case CRC_D:___output(ae,0x9A);ae->nop+=1;break;
+ case CRC_E:___output(ae,0x9B);ae->nop+=1;break;
+ case CRC_H:___output(ae,0x9C);ae->nop+=1;break;
+ case CRC_L:___output(ae,0x9D);ae->nop+=1;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x9C);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x9D);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x9C);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x9D);ae->nop+=2;break;
+ case CRC_IX:case CRC_IY:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use SBC with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n");
+ ae->idx++;
+ return;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0x9E);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=3;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0x9E);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=3;
+ } else {
+ ___output(ae,0xDE);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_V8);
+ ae->nop+=2;
+ }
+ }
+ ae->idx++;
+ } else if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_HL:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_BC:___output(ae,0xED);___output(ae,0x42);ae->nop+=4;break;
+ case CRC_DE:___output(ae,0xED);___output(ae,0x52);ae->nop+=4;break;
+ case CRC_HL:___output(ae,0xED);___output(ae,0x62);ae->nop+=4;break;
+ case CRC_SP:___output(ae,0xED);___output(ae,0x72);ae->nop+=4;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SBC HL,[BC,DE,HL,SP]\n");
+ }
+ break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SBC HL,[BC,DE,HL,SP]\n");
+ }
+ ae->idx+=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid syntax for SBC\n");
+ }
+}
+
+void _ADC(struct s_assenv *ae) {
+ if ((!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) || ((!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) && strcmp(ae->wl[ae->idx+1].w,"A")==0)) {
+ if (!ae->wl[ae->idx+1].t) ae->idx++;
+ /* also implicit A */
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_A:___output(ae,0x8F);ae->nop+=1;break;
+ case CRC_MHL:___output(ae,0x8E);ae->nop+=2;break;
+ case CRC_B:___output(ae,0x88);ae->nop+=1;break;
+ case CRC_C:___output(ae,0x89);ae->nop+=1;break;
+ case CRC_D:___output(ae,0x8A);ae->nop+=1;break;
+ case CRC_E:___output(ae,0x8B);ae->nop+=1;break;
+ case CRC_H:___output(ae,0x8C);ae->nop+=1;break;
+ case CRC_L:___output(ae,0x8D);ae->nop+=1;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x8C);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x8D);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x8C);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x8D);ae->nop+=2;break;
+ case CRC_IX:case CRC_IY:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use ADC with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n");
+ ae->idx++;
+ return;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0x8E);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=3;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0x8E);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=3;
+ } else {
+ ___output(ae,0xCE);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_V8);
+ ae->nop+=2;
+ }
+ }
+ ae->idx++;
+ } else if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_HL:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_BC:___output(ae,0xED);___output(ae,0x4A);ae->nop+=4;break;
+ case CRC_DE:___output(ae,0xED);___output(ae,0x5A);ae->nop+=4;break;
+ case CRC_HL:___output(ae,0xED);___output(ae,0x6A);ae->nop+=4;break;
+ case CRC_SP:___output(ae,0xED);___output(ae,0x7A);ae->nop+=4;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is ADC HL,[BC,DE,HL,SP]\n");
+ }
+ break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is ADC HL,[BC,DE,HL,SP]\n");
+ }
+ ae->idx+=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid syntax for ADC\n");
+ }
+}
+
+void _ADD(struct s_assenv *ae) {
+ if ((!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) || ((!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) && strcmp(ae->wl[ae->idx+1].w,"A")==0)) {
+ if (!ae->wl[ae->idx+1].t) ae->idx++;
+ /* also implicit A */
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_A:___output(ae,0x87);ae->nop+=1;break;
+ case CRC_MHL:___output(ae,0x86);ae->nop+=2;break;
+ case CRC_B:___output(ae,0x80);ae->nop+=1;break;
+ case CRC_C:___output(ae,0x81);ae->nop+=1;break;
+ case CRC_D:___output(ae,0x82);ae->nop+=1;break;
+ case CRC_E:___output(ae,0x83);ae->nop+=1;break;
+ case CRC_H:___output(ae,0x84);ae->nop+=1;break;
+ case CRC_L:___output(ae,0x85);ae->nop+=1;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x84);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x85);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x84);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x85);ae->nop+=2;break;
+ case CRC_IX:case CRC_IY:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use ADD with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n");
+ ae->idx++;
+ return;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0x86);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0x86);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else {
+ ___output(ae,0xC6);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_V8);
+ ae->nop+=2;
+ }
+ }
+ ae->idx++;
+ } else if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_HL:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_BC:___output(ae,0x09);ae->nop+=3;break;
+ case CRC_DE:___output(ae,0x19);ae->nop+=3;break;
+ case CRC_HL:___output(ae,0x29);ae->nop+=3;break;
+ case CRC_SP:___output(ae,0x39);ae->nop+=3;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is ADD HL,[BC,DE,HL,SP]\n");
+ }
+ break;
+ case CRC_IX:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_BC:___output(ae,0xDD);___output(ae,0x09);ae->nop+=4;break;
+ case CRC_DE:___output(ae,0xDD);___output(ae,0x19);ae->nop+=4;break;
+ case CRC_IX:___output(ae,0xDD);___output(ae,0x29);ae->nop+=4;break;
+ case CRC_SP:___output(ae,0xDD);___output(ae,0x39);ae->nop+=4;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is ADD IX,[BC,DE,IX,SP]\n");
+ }
+ break;
+ case CRC_IY:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_BC:___output(ae,0xFD);___output(ae,0x09);ae->nop+=4;break;
+ case CRC_DE:___output(ae,0xFD);___output(ae,0x19);ae->nop+=4;break;
+ case CRC_IY:___output(ae,0xFD);___output(ae,0x29);ae->nop+=4;break;
+ case CRC_SP:___output(ae,0xFD);___output(ae,0x39);ae->nop+=4;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is ADD IY,[BC,DE,IY,SP]\n");
+ }
+ break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is ADD [HL,IX,IY],reg16\n");
+ }
+ ae->idx+=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid syntax for ADD\n");
+ }
+}
+
+void _CP(struct s_assenv *ae) {
+ if ((!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) || ((!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) && strcmp(ae->wl[ae->idx+1].w,"A")==0)) {
+ if (!ae->wl[ae->idx+1].t) ae->idx++;
+ /* also implicit A */
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_A:___output(ae,0xBF);ae->nop+=1;break;
+ case CRC_MHL:___output(ae,0xBE);ae->nop+=2;break;
+ case CRC_B:___output(ae,0xB8);ae->nop+=1;break;
+ case CRC_C:___output(ae,0xB9);ae->nop+=1;break;
+ case CRC_D:___output(ae,0xBA);ae->nop+=1;break;
+ case CRC_E:___output(ae,0xBB);ae->nop+=1;break;
+ case CRC_H:___output(ae,0xBC);ae->nop+=1;break;
+ case CRC_L:___output(ae,0xBD);ae->nop+=1;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0xBC);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0xBD);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0xBC);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0xBD);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0xBE);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0xBE);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else {
+ ___output(ae,0xFE);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_V8);
+ ae->nop+=2;
+ }
+ }
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Syntax is CP reg8/(reg16)\n");
+ }
+}
+
+void _RET(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_NZ:___output(ae,0xC0);ae->nop+=2;break;
+ case CRC_Z:___output(ae,0xC8);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xD8);ae->nop+=2;break;
+ case CRC_NC:___output(ae,0xD0);ae->nop+=2;break;
+ case CRC_PE:___output(ae,0xE8);ae->nop+=2;break;
+ case CRC_PO:___output(ae,0xE0);ae->nop+=2;break;
+ case CRC_P:___output(ae,0xF0);ae->nop+=2;break;
+ case CRC_M:___output(ae,0xF8);ae->nop+=2;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Available flags for RET are C,NC,Z,NZ,PE,PO,P,M\n");
+ }
+ ae->idx++;
+ } else if (ae->wl[ae->idx].t==1) {
+ ___output(ae,0xC9);
+ ae->nop+=3;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid RET syntax\n");
+ }
+}
+
+void _CALL(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==0 && ae->wl[ae->idx+2].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_C:___output(ae,0xDC);ae->nop+=3;break;
+ case CRC_Z:___output(ae,0xCC);ae->nop+=3;break;
+ case CRC_NZ:___output(ae,0xC4);ae->nop+=3;break;
+ case CRC_NC:___output(ae,0xD4);ae->nop+=3;break;
+ case CRC_PE:___output(ae,0xEC);ae->nop+=3;break;
+ case CRC_PO:___output(ae,0xE4);ae->nop+=3;break;
+ case CRC_P:___output(ae,0xF4);ae->nop+=3;break;
+ case CRC_M:___output(ae,0xFC);ae->nop+=3;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Available flags for CALL are C,NC,Z,NZ,PE,PO,P,M\n");
+ }
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V16C);
+ ae->idx+=2;
+ } else if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ___output(ae,0xCD);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_V16C);
+ ae->idx++;
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid CALL syntax\n");
+ }
+}
+
+void _JR(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==0 && ae->wl[ae->idx+2].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_NZ:___output(ae,0x20);ae->nop+=2;break;
+ case CRC_C:___output(ae,0x38);ae->nop+=2;break;
+ case CRC_Z:___output(ae,0x28);ae->nop+=2;break;
+ case CRC_NC:___output(ae,0x30);ae->nop+=2;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Available flags for JR are C,NC,Z,NZ\n");
+ }
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_J8);
+ ae->idx+=2;
+ } else if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ___output(ae,0x18);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_J8);
+ ae->idx++;
+ ae->nop+=3;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid JR syntax\n");
+ }
+}
+
+void _JP(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==0 && ae->wl[ae->idx+2].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_C:___output(ae,0xDA);ae->nop+=3;break;
+ case CRC_Z:___output(ae,0xCA);ae->nop+=3;break;
+ case CRC_NZ:___output(ae,0xC2);ae->nop+=3;break;
+ case CRC_NC:___output(ae,0xD2);ae->nop+=3;break;
+ case CRC_PE:___output(ae,0xEA);ae->nop+=3;break;
+ case CRC_PO:___output(ae,0xE2);ae->nop+=3;break;
+ case CRC_P:___output(ae,0xF2);ae->nop+=3;break;
+ case CRC_M:___output(ae,0xFA);ae->nop+=3;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Available flags for JP are C,NC,Z,NZ,PE,PO,P,M\n");
+ }
+ if (!strcmp(ae->wl[ae->idx+2].w,"(IX)") || !strcmp(ae->wl[ae->idx+2].w,"(IY)")) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"conditionnal JP cannot use register adressing\n");
+ } else {
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V16);
+ }
+ ae->idx+=2;
+ } else if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_HL:case CRC_MHL:___output(ae,0xE9);ae->nop+=1;break;
+ case CRC_IX:case CRC_MIX:___output(ae,0xDD);___output(ae,0xE9);ae->nop+=2;break;
+ case CRC_IY:case CRC_MIY:___output(ae,0xFD);___output(ae,0xE9);ae->nop+=2;break;
+ default:
+ ___output(ae,0xC3);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_V16);
+ }
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid JP syntax\n");
+ }
+}
+
+
+void _DEC(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ do {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_A:___output(ae,0x3D);ae->nop+=1;break;
+ case CRC_B:___output(ae,0x05);ae->nop+=1;break;
+ case CRC_C:___output(ae,0x0D);ae->nop+=1;break;
+ case CRC_D:___output(ae,0x15);ae->nop+=1;break;
+ case CRC_E:___output(ae,0x1D);ae->nop+=1;break;
+ case CRC_H:___output(ae,0x25);ae->nop+=1;break;
+ case CRC_L:___output(ae,0x2D);ae->nop+=1;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x25);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x2D);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x25);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x2D);ae->nop+=2;break;
+ case CRC_BC:___output(ae,0x0B);ae->nop+=2;break;
+ case CRC_DE:___output(ae,0x1B);ae->nop+=2;break;
+ case CRC_HL:___output(ae,0x2B);ae->nop+=2;break;
+ case CRC_IX:___output(ae,0xDD);___output(ae,0x2B);ae->nop+=3;break;
+ case CRC_IY:___output(ae,0xFD);___output(ae,0x2B);ae->nop+=3;break;
+ case CRC_SP:___output(ae,0x3B);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0x35);ae->nop+=3;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0x35);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=6;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0x35);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=6;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use DEC with A,B,C,D,E,H,L,XH,XL,YH,YL,BC,DE,HL,SP,(HL),(IX),(IY)\n");
+ }
+ }
+ ae->idx++;
+ } while (ae->wl[ae->idx].t==0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use DEC with A,B,C,D,E,H,L,XH,XL,YH,YL,BC,DE,HL,SP,(HL),(IX),(IY)\n");
+ }
+}
+void _INC(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ do {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_A:___output(ae,0x3C);ae->nop+=1;break;
+ case CRC_B:___output(ae,0x04);ae->nop+=1;break;
+ case CRC_C:___output(ae,0x0C);ae->nop+=1;break;
+ case CRC_D:___output(ae,0x14);ae->nop+=1;break;
+ case CRC_E:___output(ae,0x1C);ae->nop+=1;break;
+ case CRC_H:___output(ae,0x24);ae->nop+=1;break;
+ case CRC_L:___output(ae,0x2C);ae->nop+=1;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x24);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x2C);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x24);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x2C);ae->nop+=2;break;
+ case CRC_BC:___output(ae,0x03);ae->nop+=2;break;
+ case CRC_DE:___output(ae,0x13);ae->nop+=2;break;
+ case CRC_HL:___output(ae,0x23);ae->nop+=2;break;
+ case CRC_IX:___output(ae,0xDD);___output(ae,0x23);ae->nop+=3;break;
+ case CRC_IY:___output(ae,0xFD);___output(ae,0x23);ae->nop+=3;break;
+ case CRC_SP:___output(ae,0x33);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0x34);ae->nop+=3;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0x34);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=6;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0x34);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=6;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use INC with A,B,C,D,E,H,L,XH,XL,YH,YL,BC,DE,HL,SP,(HL),(IX),(IY)\n");
+ }
+ }
+ ae->idx++;
+ } while (ae->wl[ae->idx].t==0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use INC with A,B,C,D,E,H,L,XH,XL,YH,YL,BC,DE,HL,SP,(HL),(IX),(IY)\n");
+ }
+}
+
+void _SUB(struct s_assenv *ae) {
+ #ifdef OPCODE
+ #undef OPCODE
+ #endif
+ #define OPCODE 0x90
+
+ if ((!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) || ((!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) && strcmp(ae->wl[ae->idx+1].w,"A")==0)) {
+ if (!ae->wl[ae->idx+1].t) ae->idx++;
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_A:___output(ae,OPCODE+7);ae->nop+=1;break;
+ case CRC_MHL:___output(ae,OPCODE+6);ae->nop+=2;break;
+ case CRC_B:___output(ae,OPCODE);ae->nop+=1;break;
+ case CRC_C:___output(ae,OPCODE+1);ae->nop+=1;break;
+ case CRC_D:___output(ae,OPCODE+2);ae->nop+=1;break;
+ case CRC_E:___output(ae,OPCODE+3);ae->nop+=1;break;
+ case CRC_H:___output(ae,OPCODE+4);ae->nop+=1;break;
+ case CRC_L:___output(ae,OPCODE+5);ae->nop+=1;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,OPCODE+4);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,OPCODE+5);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,OPCODE+4);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,OPCODE+5);ae->nop+=2;break;
+ case CRC_IX:case CRC_IY:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use SUB with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n");
+ ae->idx++;
+ return;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,OPCODE+6);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,OPCODE+6);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else {
+ ___output(ae,0xD6);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_V8);
+ ae->nop+=2;
+ }
+ }
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use SUB with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n");
+ }
+}
+void _AND(struct s_assenv *ae) {
+ #ifdef OPCODE
+ #undef OPCODE
+ #endif
+ #define OPCODE 0xA0
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_A:___output(ae,OPCODE+7);ae->nop+=1;break;
+ case CRC_MHL:___output(ae,OPCODE+6);ae->nop+=2;break;
+ case CRC_B:___output(ae,OPCODE);ae->nop+=1;break;
+ case CRC_C:___output(ae,OPCODE+1);ae->nop+=1;break;
+ case CRC_D:___output(ae,OPCODE+2);ae->nop+=1;break;
+ case CRC_E:___output(ae,OPCODE+3);ae->nop+=1;break;
+ case CRC_H:___output(ae,OPCODE+4);ae->nop+=1;break;
+ case CRC_L:___output(ae,OPCODE+5);ae->nop+=1;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,OPCODE+4);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,OPCODE+5);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,OPCODE+4);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,OPCODE+5);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,OPCODE+6);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,OPCODE+6);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else {
+ ___output(ae,0xE6);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_V8);
+ ae->nop+=2;
+ }
+ }
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use AND with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n");
+ }
+}
+void _OR(struct s_assenv *ae) {
+ #ifdef OPCODE
+ #undef OPCODE
+ #endif
+ #define OPCODE 0xB0
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_A:___output(ae,OPCODE+7);ae->nop+=1;break;
+ case CRC_MHL:___output(ae,OPCODE+6);ae->nop+=2;break;
+ case CRC_B:___output(ae,OPCODE);ae->nop+=1;break;
+ case CRC_C:___output(ae,OPCODE+1);ae->nop+=1;break;
+ case CRC_D:___output(ae,OPCODE+2);ae->nop+=1;break;
+ case CRC_E:___output(ae,OPCODE+3);ae->nop+=1;break;
+ case CRC_H:___output(ae,OPCODE+4);ae->nop+=1;break;
+ case CRC_L:___output(ae,OPCODE+5);ae->nop+=1;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,OPCODE+4);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,OPCODE+5);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,OPCODE+4);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,OPCODE+5);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,OPCODE+6);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,OPCODE+6);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else {
+ ___output(ae,0xF6);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_V8);
+ ae->nop+=2;
+ }
+ }
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use OR with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n");
+ }
+}
+void _XOR(struct s_assenv *ae) {
+ #ifdef OPCODE
+ #undef OPCODE
+ #endif
+ #define OPCODE 0xA8
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_A:___output(ae,OPCODE+7);ae->nop+=1;break;
+ case CRC_MHL:___output(ae,OPCODE+6);ae->nop+=2;break;
+ case CRC_B:___output(ae,OPCODE);ae->nop+=1;break;
+ case CRC_C:___output(ae,OPCODE+1);ae->nop+=1;break;
+ case CRC_D:___output(ae,OPCODE+2);ae->nop+=1;break;
+ case CRC_E:___output(ae,OPCODE+3);ae->nop+=1;break;
+ case CRC_H:___output(ae,OPCODE+4);ae->nop+=1;break;
+ case CRC_L:___output(ae,OPCODE+5);ae->nop+=1;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,OPCODE+4);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,OPCODE+5);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,OPCODE+4);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,OPCODE+5);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,OPCODE+6);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,OPCODE+6);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else {
+ ___output(ae,0xEE);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_V8);
+ ae->nop+=2;
+ }
+ }
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use XOR with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n");
+ }
+}
+
+
+void _POP(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ do {
+ ae->idx++;
+ switch (GetCRC(ae->wl[ae->idx].w)) {
+ case CRC_AF:___output(ae,0xF1);ae->nop+=3;break;
+ case CRC_BC:___output(ae,0xC1);ae->nop+=3;break;
+ case CRC_DE:___output(ae,0xD1);ae->nop+=3;break;
+ case CRC_HL:___output(ae,0xE1);ae->nop+=3;break;
+ case CRC_IX:___output(ae,0xDD);___output(ae,0xE1);ae->nop+=4;break;
+ case CRC_IY:___output(ae,0xFD);___output(ae,0xE1);ae->nop+=4;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use POP with AF,BC,DE,HL,IX,IY\n");
+ }
+ } while (ae->wl[ae->idx].t!=1);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"POP need at least one parameter\n");
+ }
+}
+void _PUSH(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ do {
+ ae->idx++;
+ switch (GetCRC(ae->wl[ae->idx].w)) {
+ case CRC_AF:___output(ae,0xF5);ae->nop+=4;break;
+ case CRC_BC:___output(ae,0xC5);ae->nop+=4;break;
+ case CRC_DE:___output(ae,0xD5);ae->nop+=4;break;
+ case CRC_HL:___output(ae,0xE5);ae->nop+=4;break;
+ case CRC_IX:___output(ae,0xDD);___output(ae,0xE5);ae->nop+=5;break;
+ case CRC_IY:___output(ae,0xFD);___output(ae,0xE5);ae->nop+=5;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use PUSH with AF,BC,DE,HL,IX,IY\n");
+ }
+ } while (ae->wl[ae->idx].t!=1);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"PUSH need at least one parameter\n");
+ }
+}
+
+void _IM(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ /* la valeur du parametre va definir l'opcode du IM */
+ ___output(ae,0xED);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IM);
+ ae->idx++;
+ ae->nop+=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IM need one parameter\n");
+ }
+}
+
+void _RLCA(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0x7);
+ ae->nop+=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RLCA does not need parameter\n");
+ }
+}
+void _RRCA(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xF);
+ ae->nop+=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RRCA does not need parameter\n");
+ }
+}
+void _NEG(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0x44);
+ ae->nop+=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"NEG does not need parameter\n");
+ }
+}
+void _DAA(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0x27);
+ ae->nop+=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DAA does not need parameter\n");
+ }
+}
+void _CPL(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0x2F);
+ ae->nop+=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CPL does not need parameter\n");
+ }
+}
+void _RETI(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0x4D);
+ ae->nop+=4;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RETI does not need parameter\n");
+ }
+}
+void _SCF(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0x37);
+ ae->nop+=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SCF does not need parameter\n");
+ }
+}
+void _LDD(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0xA8);
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LDD does not need parameter\n");
+ }
+}
+void _LDDR(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0xB8);
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LDDR does not need parameter\n");
+ }
+}
+void _LDI(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0xA0);
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LDI does not need parameter\n");
+ }
+}
+void _LDIR(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0xB0);
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LDIR does not need parameter\n");
+ }
+}
+void _CCF(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0x3F);
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CCF does not need parameter\n");
+ }
+}
+void _CPD(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0xA9);
+ ae->nop+=4;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CPD does not need parameter\n");
+ }
+}
+void _CPDR(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0xB9);
+ ae->nop+=4;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CPDR does not need parameter\n");
+ }
+}
+void _CPI(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0xA1);
+ ae->nop+=4;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CPI does not need parameter\n");
+ }
+}
+void _CPIR(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0xB1);
+ ae->nop+=4;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CPIR does not need parameter\n");
+ }
+}
+void _OUTD(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0xAB);
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"OUTD does not need parameter\n");
+ }
+}
+void _OTDR(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0xBB);
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"OTDR does not need parameter\n");
+ }
+}
+void _OUTI(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0xA3);
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"OUTI does not need parameter\n");
+ }
+}
+void _OTIR(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0xB3);
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"OTIR does not need parameter\n");
+ }
+}
+void _RETN(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0x45);
+ ae->nop+=4;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RETN does not need parameter\n");
+ }
+}
+void _IND(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0xAA);
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IND does not need parameter\n");
+ }
+}
+void _INDR(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0xBA);
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INDR does not need parameter\n");
+ }
+}
+void _INI(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xED);
+ ___output(ae,0xA2);
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INI does not need parameter\n");
+ }
+}
+void _INIR(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t==1) {
+ ___output(ae,0xED);
+ ___output(ae,0xB2);
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INIR does not need parameter\n");
+ }
+}
+void _EXX(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t==1) {
+ ___output(ae,0xD9);
+ ae->nop+=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"EXX does not need parameter\n");
+ }
+}
+void _HALT(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t==1) {
+ ___output(ae,0x76);
+ ae->nop+=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"HALT does not need parameter\n");
+ }
+}
+
+void _RLA(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t==1) {
+ ___output(ae,0x17);
+ ae->nop+=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RLA does not need parameter\n");
+ }
+}
+void _RRA(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t==1) {
+ ___output(ae,0x1F);
+ ae->nop+=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RRA does not need parameter\n");
+ }
+}
+void _RLD(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t==1) {
+ ___output(ae,0xED);
+ ___output(ae,0x6F);
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RLD does not need parameter\n");
+ }
+}
+void _RRD(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t==1) {
+ ___output(ae,0xED);
+ ___output(ae,0x67);
+ ae->nop+=5;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RRD does not need parameter\n");
+ }
+}
+
+
+void _EXA(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t==1) {
+ ___output(ae,0x08);ae->nop+=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"EXA alias does not need parameter\n");
+ }
+}
+
+void _NOP(struct s_assenv *ae) {
+ int o;
+
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0x00);
+ ae->nop+=1;
+ } else if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ o=RoundComputeExpressionCore(ae,ae->wl[ae->idx+1].w,ae->codeadr,0);
+ if (o>=0) {
+ while (o>0) {
+ ___output(ae,0x00);
+ ae->nop+=1;
+ o--;
+ }
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"NOP is supposed to be used without parameter or with one optional parameter\n");
+ }
+}
+void _DI(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xF3);
+ ae->nop+=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DI does not need parameter\n");
+ }
+}
+void _EI(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___output(ae,0xFB);
+ ae->nop+=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"EI does not need parameter\n");
+ }
+}
+
+void _RST(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t!=2) {
+ if (!strcmp(ae->wl[ae->idx+1].w,"(IY)") || !strcmp(ae->wl[ae->idx+1].w,"(IX)")) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RST cannot use IX or IY\n");
+ } else {
+ /* la valeur du parametre va definir l'opcode du RST */
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_RST);
+ }
+ ae->idx++;
+ ae->nop+=4;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RST need one parameter\n");
+ }
+}
+
+void _DJNZ(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ if (IsRegister(ae->wl[ae->idx+1].w)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DJNZ cannot use register\n");
+ } else if (strcmp("(IX)",ae->wl[ae->idx+1].w)==0 || strcmp("(IY)",ae->wl[ae->idx+1].w)==0) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DJNZ cannot use register\n");
+ } else {
+ ___output(ae,0x10);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_J8);
+ ae->nop+=3;
+ }
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DJNZ need one parameter\n");
+ }
+}
+
+void _LD(struct s_assenv *ae) {
+ /* on check qu'il y a au moins deux parametres */
+ if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_A:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_I:___output(ae,0xED);___output(ae,0x57);ae->nop+=3;break;
+ case CRC_R:___output(ae,0xED);___output(ae,0x5F);ae->nop+=3;break;
+ case CRC_B:___output(ae,0x78);ae->nop+=1;break;
+ case CRC_C:___output(ae,0x79);ae->nop+=1;break;
+ case CRC_D:___output(ae,0x7A);ae->nop+=1;break;
+ case CRC_E:___output(ae,0x7B);ae->nop+=1;break;
+ case CRC_H:___output(ae,0x7C);ae->nop+=1;break;
+ case CRC_L:___output(ae,0x7D);ae->nop+=1;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x7C);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x7D);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x7C);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x7D);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0x7E);ae->nop+=2;break;
+ case CRC_A:___output(ae,0x7F);ae->nop+=1;break;
+ case CRC_MBC:___output(ae,0x0A);ae->nop+=2;break;
+ case CRC_MDE:___output(ae,0x1A);ae->nop+=2;break;
+ default:
+ /* (ix+expression) (iy+expression) (expression) expression */
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0x7E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0x7E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else if (StringIsMem(ae->wl[ae->idx+2].w)) {
+ ___output(ae,0x3A);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V16);
+ ae->nop+=4;
+ } else {
+ ___output(ae,0x3E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V8);
+ ae->nop+=2;
+ }
+ }
+ break;
+ case CRC_I:
+ if (GetCRC(ae->wl[ae->idx+2].w)==CRC_A) {
+ ___output(ae,0xED);___output(ae,0x47);
+ ae->nop+=3;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LD I,A only\n");
+ }
+ break;
+ case CRC_R:
+ if (GetCRC(ae->wl[ae->idx+2].w)==CRC_A) {
+ ___output(ae,0xED);___output(ae,0x4F);
+ ae->nop+=3;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LD R,A only\n");
+ }
+ break;
+ case CRC_B:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0x40);ae->nop+=1;break;
+ case CRC_C:___output(ae,0x41);ae->nop+=1;break;
+ case CRC_D:___output(ae,0x42);ae->nop+=1;break;
+ case CRC_E:___output(ae,0x43);ae->nop+=1;break;
+ case CRC_H:___output(ae,0x44);ae->nop+=1;break;
+ case CRC_L:___output(ae,0x45);ae->nop+=1;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x44);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x45);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x44);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x45);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0x46);ae->nop+=2;break;
+ case CRC_A:___output(ae,0x47);ae->nop+=1;break;
+ default:
+ /* (ix+expression) (iy+expression) expression */
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0x46);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0x46);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else {
+ ___output(ae,0x06);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V8);
+ ae->nop+=2;
+ }
+ }
+ break;
+ case CRC_C:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0x48);ae->nop+=1;break;
+ case CRC_C:___output(ae,0x49);ae->nop+=1;break;
+ case CRC_D:___output(ae,0x4A);ae->nop+=1;break;
+ case CRC_E:___output(ae,0x4B);ae->nop+=1;break;
+ case CRC_H:___output(ae,0x4C);ae->nop+=1;break;
+ case CRC_L:___output(ae,0x4D);ae->nop+=1;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x4C);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x4D);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x4C);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x4D);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0x4E);ae->nop+=2;break;
+ case CRC_A:___output(ae,0x4F);ae->nop+=1;break;
+ default:
+ /* (ix+expression) (iy+expression) expression */
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0x4E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0x4E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else {
+ ___output(ae,0x0E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V8);
+ ae->nop+=2;
+ }
+ }
+ break;
+ case CRC_D:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0x50);ae->nop+=1;break;
+ case CRC_C:___output(ae,0x51);ae->nop+=1;break;
+ case CRC_D:___output(ae,0x52);ae->nop+=1;break;
+ case CRC_E:___output(ae,0x53);ae->nop+=1;break;
+ case CRC_H:___output(ae,0x54);ae->nop+=1;break;
+ case CRC_L:___output(ae,0x55);ae->nop+=1;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x54);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x55);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x54);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x55);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0x56);ae->nop+=2;break;
+ case CRC_A:___output(ae,0x57);ae->nop+=1;break;
+ default:
+ /* (ix+expression) (iy+expression) expression */
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0x56);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0x56);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else {
+ ___output(ae,0x16);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V8);
+ ae->nop+=2;
+ }
+ }
+ break;
+ case CRC_E:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0x58);ae->nop+=1;break;
+ case CRC_C:___output(ae,0x59);ae->nop+=1;break;
+ case CRC_D:___output(ae,0x5A);ae->nop+=1;break;
+ case CRC_E:___output(ae,0x5B);ae->nop+=1;break;
+ case CRC_H:___output(ae,0x5C);ae->nop+=1;break;
+ case CRC_L:___output(ae,0x5D);ae->nop+=1;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x5C);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x5D);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x5C);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x5D);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0x5E);ae->nop+=2;break;
+ case CRC_A:___output(ae,0x5F);ae->nop+=1;break;
+ default:
+ /* (ix+expression) (iy+expression) expression */
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0x5E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0x5E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else {
+ ___output(ae,0x1E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V8);
+ ae->nop+=2;
+ }
+ }
+ break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0xFD);___output(ae,0x60);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xFD);___output(ae,0x61);ae->nop+=2;break;
+ case CRC_D:___output(ae,0xFD);___output(ae,0x62);ae->nop+=2;break;
+ case CRC_E:___output(ae,0xFD);___output(ae,0x63);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x64);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x65);ae->nop+=2;break;
+ case CRC_A:___output(ae,0xFD);___output(ae,0x67);ae->nop+=2;break;
+ default:
+ ___output(ae,0xFD);___output(ae,0x26);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V8);
+ ae->nop+=3;
+ }
+ break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0xFD);___output(ae,0x68);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xFD);___output(ae,0x69);ae->nop+=2;break;
+ case CRC_D:___output(ae,0xFD);___output(ae,0x6A);ae->nop+=2;break;
+ case CRC_E:___output(ae,0xFD);___output(ae,0x6B);ae->nop+=2;break;
+ case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x6C);ae->nop+=2;break;
+ case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x6D);ae->nop+=2;break;
+ case CRC_A:___output(ae,0xFD);___output(ae,0x6F);ae->nop+=2;break;
+ default:
+ ___output(ae,0xFD);___output(ae,0x2E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V8);
+ ae->nop+=3;
+ }
+ break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0xDD);___output(ae,0x60);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xDD);___output(ae,0x61);ae->nop+=2;break;
+ case CRC_D:___output(ae,0xDD);___output(ae,0x62);ae->nop+=2;break;
+ case CRC_E:___output(ae,0xDD);___output(ae,0x63);ae->nop+=2;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x64);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x65);ae->nop+=2;break;
+ case CRC_A:___output(ae,0xDD);___output(ae,0x67);ae->nop+=2;break;
+ default:
+ ___output(ae,0xDD);___output(ae,0x26);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V8);
+ ae->nop+=3;
+ }
+ break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0xDD);___output(ae,0x68);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xDD);___output(ae,0x69);ae->nop+=2;break;
+ case CRC_D:___output(ae,0xDD);___output(ae,0x6A);ae->nop+=2;break;
+ case CRC_E:___output(ae,0xDD);___output(ae,0x6B);ae->nop+=2;break;
+ case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x6C);ae->nop+=2;break;
+ case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x6D);ae->nop+=2;break;
+ case CRC_A:___output(ae,0xDD);___output(ae,0x6F);ae->nop+=2;break;
+ default:
+ ___output(ae,0xDD);___output(ae,0x2E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V8);
+ ae->nop+=3;
+ }
+ break;
+ case CRC_H:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0x60);ae->nop+=1;break;
+ case CRC_C:___output(ae,0x61);ae->nop+=1;break;
+ case CRC_D:___output(ae,0x62);ae->nop+=1;break;
+ case CRC_E:___output(ae,0x63);ae->nop+=1;break;
+ case CRC_H:___output(ae,0x64);ae->nop+=1;break;
+ case CRC_L:___output(ae,0x65);ae->nop+=1;break;
+ case CRC_MHL:___output(ae,0x66);ae->nop+=2;break;
+ case CRC_A:___output(ae,0x67);ae->nop+=1;break;
+ default:
+ /* (ix+expression) (iy+expression) expression */
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0x66);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0x66);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else {
+ ___output(ae,0x26);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V8);
+ ae->nop+=2;
+ }
+ }
+ break;
+ case CRC_L:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0x68);ae->nop+=1;break;
+ case CRC_C:___output(ae,0x69);ae->nop+=1;break;
+ case CRC_D:___output(ae,0x6A);ae->nop+=1;break;
+ case CRC_E:___output(ae,0x6B);ae->nop+=1;break;
+ case CRC_H:___output(ae,0x6C);ae->nop+=1;break;
+ case CRC_L:___output(ae,0x6D);ae->nop+=1;break;
+ case CRC_MHL:___output(ae,0x6E);ae->nop+=2;break;
+ case CRC_A:___output(ae,0x6F);ae->nop+=1;break;
+ default:
+ /* (ix+expression) (iy+expression) expression */
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0x6E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0x6E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=5;
+ } else {
+ ___output(ae,0x2E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V8);
+ ae->nop+=2;
+ }
+ }
+ break;
+ case CRC_MHL:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0x70);ae->nop+=2;break;
+ case CRC_C:___output(ae,0x71);ae->nop+=2;break;
+ case CRC_D:___output(ae,0x72);ae->nop+=2;break;
+ case CRC_E:___output(ae,0x73);ae->nop+=2;break;
+ case CRC_H:___output(ae,0x74);ae->nop+=2;break;
+ case CRC_L:___output(ae,0x75);ae->nop+=2;break;
+ case CRC_A:___output(ae,0x77);ae->nop+=2;break;
+ default:
+ /* expression */
+ ___output(ae,0x36);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V8);
+ ae->nop+=3;
+ }
+ break;
+ case CRC_MBC:
+ if (GetCRC(ae->wl[ae->idx+2].w)==CRC_A) {
+ ___output(ae,0x02);
+ ae->nop+=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LD (BC),A only\n");
+ }
+ break;
+ case CRC_MDE:
+ if (GetCRC(ae->wl[ae->idx+2].w)==CRC_A) {
+ ___output(ae,0x12);
+ ae->nop+=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LD (DE),A only\n");
+ }
+ break;
+ case CRC_HL:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_BC:___output(ae,0x60);___output(ae,0x69);ae->nop+=2;break;
+ case CRC_DE:___output(ae,0x62);___output(ae,0x6B);ae->nop+=2;break;
+ case CRC_HL:___output(ae,0x64);___output(ae,0x6D);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX+",4)==0) {
+ /* enhanced LD HL,(IX+nn) */
+ ___output(ae,0xDD);___output(ae,0x66);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV81);
+ ___output(ae,0xDD);___output(ae,0x6E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=10;
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY+",4)==0) {
+ /* enhanced LD HL,(IY+nn) */
+ ___output(ae,0xFD);___output(ae,0x66);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV81);
+ ___output(ae,0xFD);___output(ae,0x6E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=10;
+ } else if (StringIsMem(ae->wl[ae->idx+2].w)) {
+ ___output(ae,0x2A);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V16);
+ ae->nop+=5;
+ } else {
+ ___output(ae,0x21);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V16);
+ ae->nop+=3;
+ }
+ }
+ break;
+ case CRC_BC:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_BC:___output(ae,0x40);___output(ae,0x49);ae->nop+=2;break;
+ case CRC_DE:___output(ae,0x42);___output(ae,0x4B);ae->nop+=2;break;
+ case CRC_HL:___output(ae,0x44);___output(ae,0x4D);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX+",4)==0) {
+ /* enhanced LD BC,(IX+nn) */
+ ___output(ae,0xDD);___output(ae,0x46);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV81);
+ ___output(ae,0xDD);___output(ae,0x4E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=10;
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY+",4)==0) {
+ /* enhanced LD BC,(IY+nn) */
+ ___output(ae,0xFD);___output(ae,0x46);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV81);
+ ___output(ae,0xFD);___output(ae,0x4E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=10;
+ } else if (StringIsMem(ae->wl[ae->idx+2].w)) {
+ ___output(ae,0xED);___output(ae,0x4B);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV16);
+ ae->nop+=6;
+ } else {
+ ___output(ae,0x01);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V16);
+ ae->nop+=3;
+ }
+ }
+ break;
+ case CRC_DE:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_BC:___output(ae,0x50);___output(ae,0x59);ae->nop+=2;break;
+ case CRC_DE:___output(ae,0x52);___output(ae,0x5B);ae->nop+=2;break;
+ case CRC_HL:___output(ae,0x54);___output(ae,0x5D);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX+",4)==0) {
+ /* enhanced LD DE,(IX+nn) */
+ ___output(ae,0xDD);___output(ae,0x56);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV81);
+ ___output(ae,0xDD);___output(ae,0x5E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=10;
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY+",4)==0) {
+ /* enhanced LD DE,(IY+nn) */
+ ___output(ae,0xFD);___output(ae,0x56);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV81);
+ ___output(ae,0xFD);___output(ae,0x5E);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ ae->nop+=10;
+ } else if (StringIsMem(ae->wl[ae->idx+2].w)) {
+ ___output(ae,0xED);___output(ae,0x5B);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV16);
+ ae->nop+=6;
+ } else {
+ ___output(ae,0x11);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V16);
+ ae->nop+=3;
+ }
+ }
+ break;
+ case CRC_IX:
+ if (StringIsMem(ae->wl[ae->idx+2].w)) {
+ ___output(ae,0xDD);___output(ae,0x2A);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV16);
+ ae->nop+=6;
+ } else {
+ ___output(ae,0xDD);___output(ae,0x21);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV16);
+ ae->nop+=4;
+ }
+ break;
+ case CRC_IY:
+ if (StringIsMem(ae->wl[ae->idx+2].w)) {
+ ___output(ae,0xFD);___output(ae,0x2A);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV16);
+ ae->nop+=6;
+ } else {
+ ___output(ae,0xFD);___output(ae,0x21);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV16);
+ ae->nop+=4;
+ }
+ break;
+ case CRC_SP:
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_HL:___output(ae,0xF9);ae->nop+=2;break;
+ case CRC_IX:___output(ae,0xDD);___output(ae,0xF9);ae->nop+=3;break;
+ case CRC_IY:___output(ae,0xFD);___output(ae,0xF9);ae->nop+=3;break;
+ default:
+ if (StringIsMem(ae->wl[ae->idx+2].w)) {
+ ___output(ae,0xED);___output(ae,0x7B);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV16);
+ ae->nop+=6;
+ } else {
+ ___output(ae,0x31);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_V16);
+ ae->nop+=3;
+ }
+ }
+ break;
+ default:
+ /* (ix+expression) (iy+expression) (expression) expression */
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0xDD);___output(ae,0x70);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break;
+ case CRC_C:___output(ae,0xDD);___output(ae,0x71);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break;
+ case CRC_D:___output(ae,0xDD);___output(ae,0x72);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break;
+ case CRC_E:___output(ae,0xDD);___output(ae,0x73);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break;
+ case CRC_H:___output(ae,0xDD);___output(ae,0x74);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break;
+ case CRC_L:___output(ae,0xDD);___output(ae,0x75);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break;
+ case CRC_A:___output(ae,0xDD);___output(ae,0x77);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break;
+ case CRC_HL:___output(ae,0xDD);___output(ae,0x74);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV81);___output(ae,0xDD);___output(ae,0x75);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=10;break;
+ case CRC_DE:___output(ae,0xDD);___output(ae,0x72);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV81);___output(ae,0xDD);___output(ae,0x73);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=10;break;
+ case CRC_BC:___output(ae,0xDD);___output(ae,0x70);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV81);___output(ae,0xDD);___output(ae,0x71);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=10;break;
+ default:___output(ae,0xDD);___output(ae,0x36);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_3V8);
+ ae->nop+=6;
+ }
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0xFD);___output(ae,0x70);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break;
+ case CRC_C:___output(ae,0xFD);___output(ae,0x71);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break;
+ case CRC_D:___output(ae,0xFD);___output(ae,0x72);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break;
+ case CRC_E:___output(ae,0xFD);___output(ae,0x73);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break;
+ case CRC_H:___output(ae,0xFD);___output(ae,0x74);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break;
+ case CRC_L:___output(ae,0xFD);___output(ae,0x75);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break;
+ case CRC_A:___output(ae,0xFD);___output(ae,0x77);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break;
+ case CRC_HL:___output(ae,0xFD);___output(ae,0x74);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV81);___output(ae,0xFD);___output(ae,0x75);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=10;break;
+ case CRC_DE:___output(ae,0xFD);___output(ae,0x72);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV81);___output(ae,0xFD);___output(ae,0x73);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=10;break;
+ case CRC_BC:___output(ae,0xFD);___output(ae,0x70);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV81);___output(ae,0xFD);___output(ae,0x71);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=10;break;
+ default:___output(ae,0xFD);___output(ae,0x36);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_3V8);
+ ae->nop+=6;
+ }
+ } else if (StringIsMem(ae->wl[ae->idx+1].w)) {
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_A:___output(ae,0x32);PushExpression(ae,ae->idx+1,E_EXPRESSION_V16);ae->nop+=4;break;
+ case CRC_BC:___output(ae,0xED);___output(ae,0x43);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV16);ae->nop+=6;break;
+ case CRC_DE:___output(ae,0xED);___output(ae,0x53);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV16);ae->nop+=6;break;
+ case CRC_HL:___output(ae,0x22);PushExpression(ae,ae->idx+1,E_EXPRESSION_V16);ae->nop+=5;break;
+ case CRC_IX:___output(ae,0xDD);___output(ae,0x22);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV16);ae->nop+=6;break;
+ case CRC_IY:___output(ae,0xFD);___output(ae,0x22);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV16);ae->nop+=6;break;
+ case CRC_SP:___output(ae,0xED);___output(ae,0x73);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV16);ae->nop+=6;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LD (#nnnn),[A,BC,DE,HL,SP,IX,IY] only\n");
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Unknown LD format\n");
+ }
+ break;
+ }
+ ae->idx+=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LD needs two parameters\n");
+ }
+}
+
+
+void _RLC(struct s_assenv *ae) {
+ /* on check qu'il y a un ou deux parametres */
+ if (ae->wl[ae->idx+1].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_B:___output(ae,0xCB);___output(ae,0x0);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xCB);___output(ae,0x1);ae->nop+=2;break;
+ case CRC_D:___output(ae,0xCB);___output(ae,0x2);ae->nop+=2;break;
+ case CRC_E:___output(ae,0xCB);___output(ae,0x3);ae->nop+=2;break;
+ case CRC_H:___output(ae,0xCB);___output(ae,0x4);ae->nop+=2;break;
+ case CRC_L:___output(ae,0xCB);___output(ae,0x5);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0xCB);___output(ae,0x6);ae->nop+=4;break;
+ case CRC_A:___output(ae,0xCB);___output(ae,0x7);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0x6);
+ ae->nop+=7;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0x6);
+ ae->nop+=7;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RLC reg8/(HL)/(IX+n)/(IY+n)\n");
+ }
+ }
+ ae->idx++;
+ } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) {
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RLC (IX+n),reg8\n");
+ }
+ ___output(ae,0xCB);
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x0);ae->nop+=7;break;
+ case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x1);ae->nop+=7;break;
+ case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x2);ae->nop+=7;break;
+ case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x3);ae->nop+=7;break;
+ case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x4);ae->nop+=7;break;
+ case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x5);ae->nop+=7;break;
+ case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x7);ae->nop+=7;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RLC (IX+n),reg8\n");
+ }
+ ae->idx++;
+ ae->idx++;
+ }
+}
+
+void _RRC(struct s_assenv *ae) {
+ /* on check qu'il y a un ou deux parametres */
+ if (ae->wl[ae->idx+1].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_B:___output(ae,0xCB);___output(ae,0x8);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xCB);___output(ae,0x9);ae->nop+=2;break;
+ case CRC_D:___output(ae,0xCB);___output(ae,0xA);ae->nop+=2;break;
+ case CRC_E:___output(ae,0xCB);___output(ae,0xB);ae->nop+=2;break;
+ case CRC_H:___output(ae,0xCB);___output(ae,0xC);ae->nop+=2;break;
+ case CRC_L:___output(ae,0xCB);___output(ae,0xD);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0xCB);___output(ae,0xE);ae->nop+=4;break;
+ case CRC_A:___output(ae,0xCB);___output(ae,0xF);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0xE);
+ ae->nop+=7;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0xE);
+ ae->nop+=7;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RRC reg8/(HL)/(IX+n)/(IY+n)\n");
+ }
+ }
+ ae->idx++;
+ } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) {
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RRC (IX+n),reg8\n");
+ }
+ ___output(ae,0xCB);
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x8);ae->nop+=7;break;
+ case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x9);ae->nop+=7;break;
+ case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0xA);ae->nop+=7;break;
+ case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0xB);ae->nop+=7;break;
+ case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0xC);ae->nop+=7;break;
+ case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0xD);ae->nop+=7;break;
+ case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0xF);ae->nop+=7;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RRC (IX+n),reg8\n");
+ }
+ ae->idx++;
+ ae->idx++;
+ }
+}
+
+
+void _RL(struct s_assenv *ae) {
+ /* on check qu'il y a un ou deux parametres */
+ if (ae->wl[ae->idx+1].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_BC:___output(ae,0xCB);___output(ae,0x10);___output(ae,0xCB);___output(ae,0x11);ae->nop+=4;break;
+ case CRC_B:___output(ae,0xCB);___output(ae,0x10);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xCB);___output(ae,0x11);ae->nop+=2;break;
+ case CRC_DE:___output(ae,0xCB);___output(ae,0x12);___output(ae,0xCB);___output(ae,0x13);ae->nop+=4;break;
+ case CRC_D:___output(ae,0xCB);___output(ae,0x12);ae->nop+=2;break;
+ case CRC_E:___output(ae,0xCB);___output(ae,0x13);ae->nop+=2;break;
+ case CRC_HL:___output(ae,0xCB);___output(ae,0x14);___output(ae,0xCB);___output(ae,0x15);ae->nop+=4;break;
+ case CRC_H:___output(ae,0xCB);___output(ae,0x14);ae->nop+=2;break;
+ case CRC_L:___output(ae,0xCB);___output(ae,0x15);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0xCB);___output(ae,0x16);ae->nop+=4;break;
+ case CRC_A:___output(ae,0xCB);___output(ae,0x17);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0x16);
+ ae->nop+=7;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0x16);
+ ae->nop+=7;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RL reg8/(HL)/(IX+n)/(IY+n)\n");
+ }
+ }
+ ae->idx++;
+ } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) {
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RL (IX+n),reg8\n");
+ }
+ ___output(ae,0xCB);
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x10);ae->nop+=7;break;
+ case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x11);ae->nop+=7;break;
+ case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x12);ae->nop+=7;break;
+ case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x13);ae->nop+=7;break;
+ case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x14);ae->nop+=7;break;
+ case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x15);ae->nop+=7;break;
+ case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x17);ae->nop+=7;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RL (IX+n),reg8\n");
+ }
+ ae->idx++;
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RL (IX+n),reg8 or RL reg8/(HL)/(IX+n)/(IY+n)\n");
+ }
+}
+
+void _RR(struct s_assenv *ae) {
+ /* on check qu'il y a un ou deux parametres */
+ if (ae->wl[ae->idx+1].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_BC:___output(ae,0xCB);___output(ae,0x18);___output(ae,0xCB);___output(ae,0x19);ae->nop+=4;break;
+ case CRC_B:___output(ae,0xCB);___output(ae,0x18);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xCB);___output(ae,0x19);ae->nop+=2;break;
+ case CRC_DE:___output(ae,0xCB);___output(ae,0x1A);___output(ae,0xCB);___output(ae,0x1B);ae->nop+=4;break;
+ case CRC_D:___output(ae,0xCB);___output(ae,0x1A);ae->nop+=2;break;
+ case CRC_E:___output(ae,0xCB);___output(ae,0x1B);ae->nop+=2;break;
+ case CRC_HL:___output(ae,0xCB);___output(ae,0x1C);___output(ae,0xCB);___output(ae,0x1D);ae->nop+=4;break;
+ case CRC_H:___output(ae,0xCB);___output(ae,0x1C);ae->nop+=2;break;
+ case CRC_L:___output(ae,0xCB);___output(ae,0x1D);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0xCB);___output(ae,0x1E);ae->nop+=4;break;
+ case CRC_A:___output(ae,0xCB);___output(ae,0x1F);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0x1E);
+ ae->nop+=7;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0x1E);
+ ae->nop+=7;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RR reg8/(HL)/(IX+n)/(IY+n)\n");
+ }
+ }
+ ae->idx++;
+ } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) {
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RR (IX+n),reg8\n");
+ }
+ ___output(ae,0xCB);
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x18);ae->nop+=7;break;
+ case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x19);ae->nop+=7;break;
+ case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x1A);ae->nop+=7;break;
+ case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x1B);ae->nop+=7;break;
+ case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x1C);ae->nop+=7;break;
+ case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x1D);ae->nop+=7;break;
+ case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x1F);ae->nop+=7;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RR (IX+n),reg8\n");
+ }
+ ae->idx++;
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RR (IX+n),reg8 or RR reg8/(HL)/(IX+n)/(IY+n)\n");
+ }
+}
+
+
+
+
+
+void _SLA(struct s_assenv *ae) {
+ /* on check qu'il y a un ou deux parametres */
+ if (ae->wl[ae->idx+1].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_BC:___output(ae,0xCB);___output(ae,0x21);___output(ae,0xCB);___output(ae,0x10);ae->nop+=4;break; /* SLA C : RL B */
+ case CRC_B:___output(ae,0xCB);___output(ae,0x20);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xCB);___output(ae,0x21);ae->nop+=2;break;
+ case CRC_DE:___output(ae,0xCB);___output(ae,0x23);___output(ae,0xCB);___output(ae,0x12);ae->nop+=4;break; /* SLA E : RL D */
+ case CRC_D:___output(ae,0xCB);___output(ae,0x22);ae->nop+=2;break;
+ case CRC_E:___output(ae,0xCB);___output(ae,0x23);ae->nop+=2;break;
+ case CRC_HL:___output(ae,0xCB);___output(ae,0x25);___output(ae,0xCB);___output(ae,0x14);ae->nop+=4;break; /* SLA L : RL H */
+ case CRC_H:___output(ae,0xCB);___output(ae,0x24);ae->nop+=2;break;
+ case CRC_L:___output(ae,0xCB);___output(ae,0x25);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0xCB);___output(ae,0x26);ae->nop+=4;break;
+ case CRC_A:___output(ae,0xCB);___output(ae,0x27);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0x26);
+ ae->nop+=7;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0x26);
+ ae->nop+=7;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLA reg8/(HL)/(IX+n)/(IY+n)\n");
+ }
+ }
+ ae->idx++;
+ } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) {
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLL (IX+n),reg8\n");
+ }
+ ___output(ae,0xCB);
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x20);ae->nop+=7;break;
+ case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x21);ae->nop+=7;break;
+ case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x22);ae->nop+=7;break;
+ case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x23);ae->nop+=7;break;
+ case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x24);ae->nop+=7;break;
+ case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x25);ae->nop+=7;break;
+ case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x27);ae->nop+=7;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLA (IX+n),reg8\n");
+ }
+ ae->idx++;
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLA reg8/(HL)/(IX+n)/(IY+n) or SLA (IX+n),reg8\n");
+ }
+}
+
+void _SRA(struct s_assenv *ae) {
+ /* on check qu'il y a un ou deux parametres */
+ if (ae->wl[ae->idx+1].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_BC:___output(ae,0xCB);___output(ae,0x28);___output(ae,0xCB);___output(ae,0x19);ae->nop+=4;break; /* SRA B : RR C */
+ case CRC_B:___output(ae,0xCB);___output(ae,0x28);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xCB);___output(ae,0x29);ae->nop+=2;break;
+ case CRC_DE:___output(ae,0xCB);___output(ae,0x2A);___output(ae,0xCB);___output(ae,0x1B);ae->nop+=4;break; /* SRA D : RR E */
+ case CRC_D:___output(ae,0xCB);___output(ae,0x2A);ae->nop+=2;break;
+ case CRC_E:___output(ae,0xCB);___output(ae,0x2B);ae->nop+=2;break;
+ case CRC_HL:___output(ae,0xCB);___output(ae,0x2C);___output(ae,0xCB);___output(ae,0x1D);ae->nop+=4;break; /* SRA H : RR L */
+ case CRC_H:___output(ae,0xCB);___output(ae,0x2C);ae->nop+=2;break;
+ case CRC_L:___output(ae,0xCB);___output(ae,0x2D);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0xCB);___output(ae,0x2E);ae->nop+=4;break;
+ case CRC_A:___output(ae,0xCB);___output(ae,0x2F);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0x2E);
+ ae->nop+=7;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0x2E);
+ ae->nop+=7;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRA reg8/(HL)/(IX+n)/(IY+n)\n");
+ }
+ }
+ ae->idx++;
+ } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) {
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRA (IX+n),reg8\n");
+ }
+ ___output(ae,0xCB);
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x28);ae->nop+=7;break;
+ case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x29);ae->nop+=7;break;
+ case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x2A);ae->nop+=7;break;
+ case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x2B);ae->nop+=7;break;
+ case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x2C);ae->nop+=7;break;
+ case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x2D);ae->nop+=7;break;
+ case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x2F);ae->nop+=7;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRA (IX+n),reg8\n");
+ }
+ ae->idx++;
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRA reg8/(HL)/(IX+n)/(IY+n) or SRA (IX+n),reg8\n");
+ }
+}
+
+
+void _SLL(struct s_assenv *ae) {
+ /* on check qu'il y a un ou deux parametres */
+ if (ae->wl[ae->idx+1].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_BC:___output(ae,0xCB);___output(ae,0x31);___output(ae,0xCB);___output(ae,0x10);ae->nop+=4;break; /* SLL C : RL B */
+ case CRC_B:___output(ae,0xCB);___output(ae,0x30);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xCB);___output(ae,0x31);ae->nop+=2;break;
+ case CRC_DE:___output(ae,0xCB);___output(ae,0x33);___output(ae,0xCB);___output(ae,0x12);ae->nop+=4;break; /* SLL E : RL D */
+ case CRC_D:___output(ae,0xCB);___output(ae,0x32);ae->nop+=2;break;
+ case CRC_E:___output(ae,0xCB);___output(ae,0x33);ae->nop+=2;break;
+ case CRC_HL:___output(ae,0xCB);___output(ae,0x35);___output(ae,0xCB);___output(ae,0x14);ae->nop+=4;break; /* SLL L : RL H */
+ case CRC_H:___output(ae,0xCB);___output(ae,0x34);ae->nop+=2;break;
+ case CRC_L:___output(ae,0xCB);___output(ae,0x35);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0xCB);___output(ae,0x36);ae->nop+=4;break;
+ case CRC_A:___output(ae,0xCB);___output(ae,0x37);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0x36);
+ ae->nop+=7;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0x36);
+ ae->nop+=7;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLL reg8/(HL)/(IX+n)/(IY+n)\n");
+ }
+ }
+ ae->idx++;
+ } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) {
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLL (IX+n),reg8\n");
+ }
+ ___output(ae,0xCB);
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x30);ae->nop+=7;break;
+ case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x31);ae->nop+=7;break;
+ case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x32);ae->nop+=7;break;
+ case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x33);ae->nop+=7;break;
+ case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x34);ae->nop+=7;break;
+ case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x35);ae->nop+=7;break;
+ case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x37);ae->nop+=7;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLL (IX+n),reg8\n");
+ }
+ ae->idx++;
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLL reg8/(HL)/(IX+n)/(IY+n) or SLL (IX+n),reg8\n");
+ }
+}
+
+void _SRL(struct s_assenv *ae) {
+ /* on check qu'il y a un ou deux parametres */
+ if (ae->wl[ae->idx+1].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+1].w)) {
+ case CRC_BC:___output(ae,0xCB);___output(ae,0x38);___output(ae,0xCB);___output(ae,0x11);ae->nop+=4;break; /* SRL B : RL C */
+ case CRC_B:___output(ae,0xCB);___output(ae,0x38);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xCB);___output(ae,0x39);ae->nop+=2;break;
+ case CRC_DE:___output(ae,0xCB);___output(ae,0x3A);___output(ae,0xCB);___output(ae,0x13);ae->nop+=4;break; /* SRL D : RL E */
+ case CRC_D:___output(ae,0xCB);___output(ae,0x3A);ae->nop+=2;break;
+ case CRC_E:___output(ae,0xCB);___output(ae,0x3B);ae->nop+=2;break;
+ case CRC_HL:___output(ae,0xCB);___output(ae,0x3C);___output(ae,0xCB);___output(ae,0x15);ae->nop+=4;break; /* SRL H : RL L */
+ case CRC_H:___output(ae,0xCB);___output(ae,0x3C);ae->nop+=2;break;
+ case CRC_L:___output(ae,0xCB);___output(ae,0x3D);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0xCB);___output(ae,0x3E);ae->nop+=4;break;
+ case CRC_A:___output(ae,0xCB);___output(ae,0x3F);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0x3E);
+ ae->nop+=7;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);
+ ___output(ae,0x3E);
+ ae->nop+=7;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRL reg8/(HL)/(IX+n)/(IY+n)\n");
+ }
+ }
+ ae->idx++;
+ } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) {
+ if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) {
+ ___output(ae,0xDD);
+ } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) {
+ ___output(ae,0xFD);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRL (IX+n),reg8\n");
+ }
+ ___output(ae,0xCB);
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x38);ae->nop+=7;break;
+ case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x39);ae->nop+=7;break;
+ case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x3A);ae->nop+=7;break;
+ case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x3B);ae->nop+=7;break;
+ case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x3C);ae->nop+=7;break;
+ case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x3D);ae->nop+=7;break;
+ case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x3F);ae->nop+=7;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRL (IX+n),reg8\n");
+ }
+ ae->idx++;
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRL reg8/(HL)/(IX+n)/(IY+n) or SRL (IX+n),reg8\n");
+ }
+}
+
+
+void _BIT(struct s_assenv *ae) {
+ int o;
+ /* on check qu'il y a deux ou trois parametres
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ o=RoundComputeExpressionCore(ae,ae->wl[ae->idx+1].w,ae->codeadr,0);*/
+
+ o=0;
+ if (o<0 || o>7) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is BIT <value from 0 to 7>,... (%d)\n",o);
+ } else {
+ o=0x40+o*8;
+ if (ae->wl[ae->idx+1].t==0 && ae->wl[ae->idx+2].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x0+o);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x1+o);ae->nop+=2;break;
+ case CRC_D:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x2+o);ae->nop+=2;break;
+ case CRC_E:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x3+o);ae->nop+=2;break;
+ case CRC_H:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x4+o);ae->nop+=2;break;
+ case CRC_L:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x5+o);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x6+o);ae->nop+=3;break;
+ case CRC_A:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x7+o);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x6+o);
+ ae->nop+=6;
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x6+o);
+ ae->nop+=6;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is BIT n,reg8/(HL)/(IX+n)/(IY+n)\n");
+ }
+ }
+ ae->idx+=2;
+ } else if (!ae->wl[ae->idx+1].t && !ae->wl[ae->idx+2].t && ae->wl[ae->idx+3].t==1) {
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) {
+ ___output(ae,0xDD);
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) {
+ ___output(ae,0xFD);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is BIT (IX+n),reg8\n");
+ }
+ ___output(ae,0xCB);
+ switch (GetCRC(ae->wl[ae->idx+3].w)) {
+ case CRC_B:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x0+o);ae->nop+=6;break;
+ case CRC_C:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x1+o);ae->nop+=6;break;
+ case CRC_D:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x2+o);ae->nop+=6;break;
+ case CRC_E:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x3+o);ae->nop+=6;break;
+ case CRC_H:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x4+o);ae->nop+=6;break;
+ case CRC_L:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x5+o);ae->nop+=6;break;
+ case CRC_A:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x7+o);ae->nop+=6;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is BIT n,(IX+n),reg8\n");
+ }
+ ae->idx+=3;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is BIT n,reg8/(HL)/(IX+n)[,reg8]/(IY+n)[,reg8]\n");
+ }
+ }
+}
+
+void _RES(struct s_assenv *ae) {
+ int o;
+ /* on check qu'il y a deux ou trois parametres
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ o=RoundComputeExpressionCore(ae,ae->wl[ae->idx+1].w,ae->codeadr,0); */
+ o=0;
+ if (o<0 || o>7) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RES <value from 0 to 7>,... (%d)\n",o);
+ } else {
+ o=0x80+o*8;
+ if (ae->wl[ae->idx+1].t==0 && ae->wl[ae->idx+2].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x0+o);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x1+o);ae->nop+=2;break;
+ case CRC_D:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x2+o);ae->nop+=2;break;
+ case CRC_E:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x3+o);ae->nop+=2;break;
+ case CRC_H:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x4+o);ae->nop+=2;break;
+ case CRC_L:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x5+o);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x6+o);ae->nop+=4;break;
+ case CRC_A:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x7+o);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x6+o);
+ ae->nop+=7;
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x6+o);
+ ae->nop+=7;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RES n,reg8/(HL)/(IX+n)/(IY+n)\n");
+ }
+ }
+ ae->idx+=2;
+ } else if (!ae->wl[ae->idx+1].t && !ae->wl[ae->idx+2].t && ae->wl[ae->idx+3].t==1) {
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) {
+ ___output(ae,0xDD);
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) {
+ ___output(ae,0xFD);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RES n,(IX+n),reg8\n");
+ }
+ ___output(ae,0xCB);
+ switch (GetCRC(ae->wl[ae->idx+3].w)) {
+ case CRC_B:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x0+o);ae->nop+=7;break;
+ case CRC_C:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x1+o);ae->nop+=7;break;
+ case CRC_D:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x2+o);ae->nop+=7;break;
+ case CRC_E:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x3+o);ae->nop+=7;break;
+ case CRC_H:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x4+o);ae->nop+=7;break;
+ case CRC_L:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x5+o);ae->nop+=7;break;
+ case CRC_A:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x7+o);ae->nop+=7;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RES n,(IX+n),reg8\n");
+ }
+ ae->idx+=3;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RES n,reg8/(HL)/(IX+n)[,reg8]/(IY+n)[,reg8]\n");
+ }
+ }
+}
+
+void _SET(struct s_assenv *ae) {
+ int o;
+ /* on check qu'il y a deux ou trois parametres
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ o=RoundComputeExpressionCore(ae,ae->wl[ae->idx+1].w,ae->codeadr,0); */
+ o=0;
+ if (o<0 || o>7) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SET <value from 0 to 7>,... (%d)\n",o);
+ } else {
+ o=0xC0+o*8;
+ if (ae->wl[ae->idx+1].t==0 && ae->wl[ae->idx+2].t==1) {
+ switch (GetCRC(ae->wl[ae->idx+2].w)) {
+ case CRC_B:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x0+o);ae->nop+=2;break;
+ case CRC_C:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x1+o);ae->nop+=2;break;
+ case CRC_D:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x2+o);ae->nop+=2;break;
+ case CRC_E:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x3+o);ae->nop+=2;break;
+ case CRC_H:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x4+o);ae->nop+=2;break;
+ case CRC_L:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x5+o);ae->nop+=2;break;
+ case CRC_MHL:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x6+o);ae->nop+=4;break;
+ case CRC_A:___output(ae,0xCB);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x7+o);ae->nop+=2;break;
+ default:
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) {
+ ___output(ae,0xDD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x6+o);
+ ae->nop+=7;
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) {
+ ___output(ae,0xFD);___output(ae,0xCB);
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x6+o);
+ ae->nop+=7;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SET n,reg8/(HL)/(IX+n)/(IY+n)\n");
+ }
+ }
+ ae->idx+=2;
+ } else if (!ae->wl[ae->idx+1].t && !ae->wl[ae->idx+2].t && ae->wl[ae->idx+3].t==1) {
+ if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) {
+ ___output(ae,0xDD);
+ } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) {
+ ___output(ae,0xFD);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SET n,(IX+n),reg8\n");
+ }
+ ___output(ae,0xCB);
+ switch (GetCRC(ae->wl[ae->idx+3].w)) {
+ case CRC_B:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x0+o);ae->nop+=7;break;
+ case CRC_C:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x1+o);ae->nop+=7;break;
+ case CRC_D:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x2+o);ae->nop+=7;break;
+ case CRC_E:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x3+o);ae->nop+=7;break;
+ case CRC_H:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x4+o);ae->nop+=7;break;
+ case CRC_L:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x5+o);ae->nop+=7;break;
+ case CRC_A:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);PushExpression(ae,ae->idx+1,E_EXPRESSION_BRS);___output(ae,0x7+o);ae->nop+=7;break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SET n,(IX+n),reg8\n");
+ }
+ ae->idx+=3;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SET n,reg8/(HL)/(IX+n)[,reg8]/(IY+n)[,reg8]\n");
+ }
+ }
+}
+
+void _DEFS(struct s_assenv *ae) {
+ int i,r,v;
+ if (ae->wl[ae->idx].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Syntax is DEFS repeat,value or DEFS repeat\n");
+ } else do {
+ ae->idx++;
+ if (!ae->wl[ae->idx].t) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0);
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); /* doing FastTranslate but not a complete evaluation */
+ r=RoundComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->codeadr,0);
+ if (r<0) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFS size must be greater or equal to zero\n");
+ }
+ for (i=0;i<r;i++) {
+ /* keep flexibility */
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_0V8);
+ ae->nop+=1;
+ }
+ ae->idx++;
+ } else if (ae->wl[ae->idx].t==1) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0);
+ r=RoundComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->codeadr,0);
+ v=0;
+ if (r<0) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFS size must be greater or equal to zero\n");
+ }
+ for (i=0;i<r;i++) {
+ ___output(ae,v);
+ ae->nop+=1;
+ }
+ }
+ } while (!ae->wl[ae->idx].t);
+}
+
+void _DEFS_struct(struct s_assenv *ae) {
+ int i,r,v;
+ if (ae->wl[ae->idx].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Syntax is DEFS repeat,value or DEFS repeat\n");
+ } else do {
+ ae->idx++;
+ if (!ae->wl[ae->idx].t) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0);
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ r=RoundComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->codeadr,0);
+ v=RoundComputeExpressionCore(ae,ae->wl[ae->idx+1].w,ae->codeadr,0);
+ if (r<0) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFS size must be greater or equal to zero\n");
+ }
+ for (i=0;i<r;i++) {
+ ___output(ae,v);
+ ae->nop+=1;
+ }
+ ae->idx++;
+ } else if (ae->wl[ae->idx].t==1) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0);
+ r=RoundComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->codeadr,0);
+ v=0;
+ if (r<0) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFS size must be greater or equal to zero\n");
+ }
+ for (i=0;i<r;i++) {
+ ___output(ae,v);
+ ae->nop+=1;
+ }
+ }
+ } while (!ae->wl[ae->idx].t);
+}
+
+void _STR(struct s_assenv *ae) {
+ unsigned char c;
+ int i,tquote;
+
+ if (!ae->wl[ae->idx].t) {
+ do {
+ ae->idx++;
+ if ((tquote=StringIsQuote(ae->wl[ae->idx].w))!=0) {
+ i=1;
+ while (ae->wl[ae->idx].w[i] && ae->wl[ae->idx].w[i]!=tquote) {
+ if (ae->wl[ae->idx].w[i]=='\\') {
+ i++;
+ /* no conversion on escaped chars */
+ c=ae->wl[ae->idx].w[i];
+ switch (c) {
+ case 'b':c='\b';break;
+ case 'v':c='\v';break;
+ case 'f':c='\f';break;
+ case '0':c='\0';break;
+ case 'r':c='\r';break;
+ case 'n':c='\n';break;
+ case 't':c='\t';break;
+ default:break;
+ }
+ if (ae->wl[ae->idx].w[i+1]!=tquote) {
+ ___output(ae,c);
+ } else {
+ ___output(ae,c|0x80);
+ }
+ } else {
+ /* charset conversion on the fly */
+ if (ae->wl[ae->idx].w[i+1]!=tquote) {
+ ___output(ae,ae->charset[(int)ae->wl[ae->idx].w[i]]);
+ } else {
+ ___output(ae,ae->charset[(int)ae->wl[ae->idx].w[i]]|0x80);
+ }
+ }
+
+ i++;
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"STR handle only quoted strings!\n");
+ }
+ } while (ae->wl[ae->idx].t==0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"STR needs one or more quotes parameters\n");
+ }
+}
+
+void _DEFR(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ do {
+ ae->idx++;
+ PushExpression(ae,ae->idx,E_EXPRESSION_0VR);
+ } while (ae->wl[ae->idx].t==0);
+ } else {
+ if (ae->getstruct) {
+ ___output(ae,0);___output(ae,0);___output(ae,0);___output(ae,0);___output(ae,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFR needs one or more parameters\n");
+ }
+ }
+}
+void _DEFR_struct(struct s_assenv *ae) {
+ unsigned char *rc;
+ double v;
+ if (!ae->wl[ae->idx].t) {
+ do {
+ ae->idx++;
+ /* conversion des symboles connus */
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0);
+ /* calcul de la valeur définitive de l'expression */
+ v=ComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->outputadr,0);
+ /* conversion en réel Amsdos */
+ rc=__internal_MakeAmsdosREAL(ae,v,0);
+ ___output(ae,rc[0]);___output(ae,rc[1]);___output(ae,rc[2]);___output(ae,rc[3]);___output(ae,rc[4]);
+ } while (ae->wl[ae->idx].t==0);
+ } else {
+ if (ae->getstruct) {
+ ___output(ae,0);___output(ae,0);___output(ae,0);___output(ae,0);___output(ae,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFR needs one or more parameters\n");
+ }
+ }
+}
+
+void _DEFB(struct s_assenv *ae) {
+ int i,tquote;
+ unsigned char c;
+ if (!ae->wl[ae->idx].t) {
+ do {
+ ae->idx++;
+ if ((tquote=StringIsQuote(ae->wl[ae->idx].w))!=0) {
+ i=1;
+ while (ae->wl[ae->idx].w[i] && ae->wl[ae->idx].w[i]!=tquote) {
+ if (ae->wl[ae->idx].w[i]=='\\') {
+ i++;
+ /* no conversion on escaped chars */
+ c=ae->wl[ae->idx].w[i];
+ switch (c) {
+ case 'b':___output(ae,'\b');break;
+ case 'v':___output(ae,'\v');break;
+ case 'f':___output(ae,'\f');break;
+ case '0':___output(ae,'\0');break;
+ case 'r':___output(ae,'\r');break;
+ case 'n':___output(ae,'\n');break;
+ case 't':___output(ae,'\t');break;
+ default:
+ ___output(ae,c);
+ ae->nop+=1;
+ }
+ } else {
+ /* charset conversion on the fly */
+ ___output(ae,ae->charset[(int)ae->wl[ae->idx].w[i]]);
+ ae->nop+=1;
+ }
+ i++;
+ }
+ } else {
+ PushExpression(ae,ae->idx,E_EXPRESSION_0V8);
+ ae->nop+=1;
+ }
+ } while (ae->wl[ae->idx].t==0);
+ } else {
+ if (ae->getstruct) {
+ ___output(ae,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFB needs one or more parameters\n");
+ }
+ }
+}
+void _DEFB_struct(struct s_assenv *ae) {
+ int i,tquote;
+ unsigned char c;
+ if (!ae->wl[ae->idx].t) {
+ do {
+ ae->idx++;
+ if ((tquote=StringIsQuote(ae->wl[ae->idx].w))!=0) {
+ i=1;
+ while (ae->wl[ae->idx].w[i] && ae->wl[ae->idx].w[i]!=tquote) {
+ if (ae->wl[ae->idx].w[i]=='\\') {
+ i++;
+ /* no conversion on escaped chars */
+ c=ae->wl[ae->idx].w[i];
+ switch (c) {
+ case 'b':___output(ae,'\b');break;
+ case 'v':___output(ae,'\v');break;
+ case 'f':___output(ae,'\f');break;
+ case '0':___output(ae,'\0');break;
+ case 'r':___output(ae,'\r');break;
+ case 'n':___output(ae,'\n');break;
+ case 't':___output(ae,'\t');break;
+ default:
+ ___output(ae,c);
+ ae->nop+=1;
+ }
+ } else {
+ /* charset conversion on the fly */
+ ___output(ae,ae->charset[(int)ae->wl[ae->idx].w[i]]);
+ ae->nop+=1;
+ }
+ i++;
+ }
+ } else {
+ int v;
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0);
+ v=RoundComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->outputadr,0);
+ ___output(ae,v);
+ ae->nop+=1;
+ }
+ } while (ae->wl[ae->idx].t==0);
+ } else {
+ if (ae->getstruct) {
+ ___output(ae,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFB needs one or more parameters\n");
+ }
+ }
+}
+
+void _DEFW(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ do {
+ ae->idx++;
+ PushExpression(ae,ae->idx,E_EXPRESSION_0V16);
+ } while (ae->wl[ae->idx].t==0);
+ } else {
+ if (ae->getstruct) {
+ ___output(ae,0);___output(ae,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFW needs one or more parameters\n");
+ }
+ }
+}
+
+void _DEFW_struct(struct s_assenv *ae) {
+ int v;
+ if (!ae->wl[ae->idx].t) {
+ do {
+ ae->idx++;
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0);
+ v=RoundComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->outputadr,0);
+ ___output(ae,v&0xFF);___output(ae,(v>>8)&0xFF);
+ } while (ae->wl[ae->idx].t==0);
+ } else {
+ if (ae->getstruct) {
+ ___output(ae,0);___output(ae,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFW needs one or more parameters\n");
+ }
+ }
+}
+
+void _DEFI(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ do {
+ ae->idx++;
+ PushExpression(ae,ae->idx,E_EXPRESSION_0V32);
+ } while (ae->wl[ae->idx].t==0);
+ } else {
+ if (ae->getstruct) {
+ ___output(ae,0);___output(ae,0);___output(ae,0);___output(ae,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFI needs one or more parameters\n");
+ }
+ }
+}
+
+void _DEFI_struct(struct s_assenv *ae) {
+ int v;
+ if (!ae->wl[ae->idx].t) {
+ do {
+ ae->idx++;
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0);
+ v=RoundComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->outputadr,0);
+ ___output(ae,v&0xFF);___output(ae,(v>>8)&0xFF);___output(ae,(v>>16)&0xFF);___output(ae,(v>>24)&0xFF);
+ } while (ae->wl[ae->idx].t==0);
+ } else {
+ if (ae->getstruct) {
+ ___output(ae,0);___output(ae,0);___output(ae,0);___output(ae,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFI needs one or more parameters\n");
+ }
+ }
+}
+
+void _DEFB_as80(struct s_assenv *ae) {
+ int i,tquote;
+ int modadr=0;
+
+ if (!ae->wl[ae->idx].t) {
+ do {
+ ae->idx++;
+ if ((tquote=StringIsQuote(ae->wl[ae->idx].w))!=0) {
+ i=1;
+ while (ae->wl[ae->idx].w[i] && ae->wl[ae->idx].w[i]!=tquote) {
+ if (ae->wl[ae->idx].w[i]=='\\') i++;
+ /* charset conversion on the fly */
+ ___output(ae,ae->charset[(int)ae->wl[ae->idx].w[i]]);
+ ae->nop+=1;
+ ae->codeadr--;modadr++;
+ i++;
+ }
+ } else {
+ PushExpression(ae,ae->idx,E_EXPRESSION_0V8);
+ ae->codeadr--;modadr++;
+ ae->nop+=1;
+ }
+ } while (ae->wl[ae->idx].t==0);
+ ae->codeadr+=modadr;
+ } else {
+ if (ae->getstruct) {
+ ___output(ae,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFB needs one or more parameters\n");
+ }
+ }
+}
+
+void _DEFW_as80(struct s_assenv *ae) {
+ int modadr=0;
+ if (!ae->wl[ae->idx].t) {
+ do {
+ ae->idx++;
+ PushExpression(ae,ae->idx,E_EXPRESSION_0V16);
+ ae->codeadr-=2;modadr+=2;
+ } while (ae->wl[ae->idx].t==0);
+ ae->codeadr+=modadr;
+ } else {
+ if (ae->getstruct) {
+ ___output(ae,0);___output(ae,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFW needs one or more parameters\n");
+ }
+ }
+}
+
+void _DEFI_as80(struct s_assenv *ae) {
+ int modadr=0;
+ if (!ae->wl[ae->idx].t) {
+ do {
+ ae->idx++;
+ PushExpression(ae,ae->idx,E_EXPRESSION_0V32);
+ ae->codeadr-=4;modadr+=4;
+ } while (ae->wl[ae->idx].t==0);
+ ae->codeadr+=modadr;
+ } else {
+ if (ae->getstruct) {
+ ___output(ae,0);___output(ae,0);___output(ae,0);___output(ae,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFI needs one or more parameters\n");
+ }
+ }
+}
+#if 0
+void _DEFSTR(struct s_assenv *ae) {
+ int i,tquote;
+ unsigned char c;
+ if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) {
+ if (StringIsQuote(ae->wl[ae->idx+1].w) && StringIsQuote(ae->wl[ae->idx+2].w)) {
+ i=1;
+ while (ae->wl[ae->idx].w[i] && ae->wl[ae->idx].w[i]!=tquote) {
+ if (ae->wl[ae->idx].w[i]=='\\') {
+ i++;
+ /* no conversion on escaped chars */
+ c=ae->wl[ae->idx].w[i];
+ switch (c) {
+ case 'b':___output(ae,'\b');break;
+ case 'v':___output(ae,'\v');break;
+ case 'f':___output(ae,'\f');break;
+ case '0':___output(ae,'\0');break;
+ case 'r':___output(ae,'\r');break;
+ case 'n':___output(ae,'\n');break;
+ case 't':___output(ae,'\t');break;
+ default:
+ ___output(ae,c);
+ }
+ } else {
+ /* charset conversion on the fly */
+ ___output(ae,ae->charset[(int)ae->wl[ae->idx].w[i]]);
+ }
+ i++;
+ }
+ }
+ ae->idx+=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFSTR needs two parameters\n");
+ }
+}
+#endif
+
+#undef FUNC
+#define FUNC "Directive CORE"
+
+void __internal_UpdateLZBlockIfAny(struct s_assenv *ae) {
+ /* there was a crunched block opened in the previous bank */
+ if (ae->lz>=0) {
+ //ae->lzsection[ae->ilz-1].iorgzone=ae->io-1;
+ //ae->lzsection[ae->ilz-1].ibank=ae->activebank;
+ }
+ ae->lz=-1;
+}
+
+
+void __AMSDOS(struct s_assenv *ae) {
+ ae->amsdos=1;
+}
+
+void __internal_EXPORT(struct s_assenv *ae, int exportval) {
+ struct s_label *curlabel;
+ struct s_expr_dico *curdic;
+ int ialias,crc,freeflag;
+ char *localname;
+
+ if (ae->wl[ae->idx].t) {
+ /* without parameter enable/disable export */
+ ae->autorise_export=exportval;
+ } else while (!ae->wl[ae->idx].t) {
+ ae->idx++;
+ freeflag=0;
+
+ /* local label */
+ if (ae->wl[ae->idx].w[0]=='.' && ae->lastgloballabel) {
+ localname=MemMalloc(strlen(ae->wl[ae->idx].w)+1+ae->lastgloballabellen);
+ sprintf(localname,"%s%s",ae->lastgloballabel,ae->wl[ae->idx].w);
+ freeflag=1;
+ } else {
+ localname=ae->wl[ae->idx].w;
+ }
+ crc=GetCRC(localname);
+
+ if ((curlabel=SearchLabel(ae,localname,crc))!=NULL) {
+ curlabel->autorise_export=exportval;
+ ae->label[curlabel->backidx].autorise_export=exportval;
+ } else {
+ if ((curdic=SearchDico(ae,ae->wl[ae->idx].w,crc))!=NULL) {
+ curdic->autorise_export=exportval;
+ } else {
+ if ((ialias=SearchAlias(ae,crc,ae->wl[ae->idx].w))!=-1) {
+ ae->alias[ialias].autorise_export=exportval;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"(E)NOEXPORT did not found [%s] in variables, labels or aliases\n",ae->wl[ae->idx].w);
+ }
+ }
+ }
+ if (freeflag) MemFree(localname);
+ }
+}
+void __NOEXPORT(struct s_assenv *ae) {
+ __internal_EXPORT(ae,0);
+}
+void __ENOEXPORT(struct s_assenv *ae) {
+ __internal_EXPORT(ae,1);
+}
+
+void __BUILDZX(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BUILDZX does not need a parameter\n");
+ }
+ if (!ae->forcesnapshot && !ae->forcetape && !ae->forcecpr) {
+ ae->forcezx=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot select ZX output when already in Amstrad cartridge/snapshot/tape output\n");
+ }
+}
+void __BUILDCPR(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BUILDCPR does not need a parameter\n");
+ }
+ if (!ae->forcesnapshot && !ae->forcetape && !ae->forcezx) {
+ ae->forcecpr=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot select Amstrad cartridge output when already in snapshot/tape output\n");
+ }
+}
+void __BUILDSNA(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ if (strcmp(ae->wl[ae->idx+1].w,"V2")==0) {
+ ae->snapshot.version=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BUILDSNA unrecognized option\n");
+ }
+ }
+ if (!ae->forcecpr && !ae->forcetape && !ae->forcezx) {
+ ae->forcesnapshot=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot select snapshot output when already in cartridge/tape output\n");
+ }
+}
+void __BUILDROM(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BUILDROM does not need a parameter\n");
+ }
+ if (!ae->forcesnapshot && !ae->forcetape && !ae->forcezx && !ae->forcecpr) {
+ ae->forceROM=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot select Amstrad ROM output when already in snapshot/tape/zx/cartridge output\n");
+ }
+}
+void __BUILDTAPE(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BUILDTAPE does not need a parameter\n");
+ }
+ if (!ae->forcesnapshot && !ae->forcecpr && !ae->forcezx) {
+ ae->forcetape=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot select tape output when already in snapshot/cartridge output\n");
+ }
+}
+
+
+void __LZ4(struct s_assenv *ae) {
+ struct s_lz_section curlz;
+
+ if (!ae->wl[ae->idx].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LZ directive does not need any parameter\n");
+ return;
+ }
+ #ifdef NO_3RD_PARTIES
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot use 3rd parties cruncher with this version of RASM\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ FreeAssenv(ae);
+ exit(-5);
+ #endif
+
+ if (ae->lz>=0 && ae->lz<ae->ilz) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot start a new LZ section inside another one (%d)\n",GetCurrentFile(ae),ae->wl[ae->idx].l,ae->lz);
+ FreeAssenv(ae);
+ exit(-5);
+ }
+ curlz.iw=ae->idx;
+ curlz.iorgzone=ae->io-1;
+ curlz.ibank=ae->activebank;
+ curlz.memstart=ae->outputadr;
+ curlz.memend=-1;
+ curlz.lzversion=4;
+ ae->lz=ae->ilz;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->lzsection,&ae->ilz,&ae->mlz,&curlz,sizeof(curlz));
+}
+void __LZX7(struct s_assenv *ae) {
+ struct s_lz_section curlz;
+
+ if (!ae->wl[ae->idx].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LZ directive does not need any parameter\n");
+ return;
+ }
+ #ifdef NO_3RD_PARTIES
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot use 3rd parties cruncher with this version of RASM\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ FreeAssenv(ae);
+ exit(-5);
+ #endif
+
+ if (ae->lz>=0 && ae->lz<ae->ilz) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot start a new LZ section inside another one (%d)\n",GetCurrentFile(ae),ae->wl[ae->idx].l,ae->lz);
+ FreeAssenv(ae);
+ exit(-5);
+ }
+ curlz.iw=ae->idx;
+ curlz.iorgzone=ae->io-1;
+ curlz.ibank=ae->activebank;
+ curlz.memstart=ae->outputadr;
+ curlz.memend=-1;
+ curlz.lzversion=7;
+ ae->lz=ae->ilz;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->lzsection,&ae->ilz,&ae->mlz,&curlz,sizeof(curlz));
+}
+void __LZEXO(struct s_assenv *ae) {
+ struct s_lz_section curlz;
+
+ if (!ae->wl[ae->idx].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LZ directive does not need any parameter\n");
+ return;
+ }
+ #ifdef NO_3RD_PARTIES
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot use 3rd parties cruncher with this version of RASM\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ FreeAssenv(ae);
+ exit(-5);
+ #endif
+
+ if (ae->lz>=0 && ae->lz<ae->ilz) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot start a new LZ section inside another one (%d)\n",GetCurrentFile(ae),ae->wl[ae->idx].l,ae->lz);
+ FreeAssenv(ae);
+ exit(-5);
+ }
+ curlz.iw=ae->idx;
+ curlz.iorgzone=ae->io-1;
+ curlz.ibank=ae->activebank;
+ curlz.memstart=ae->outputadr;
+ curlz.memend=-1;
+ curlz.lzversion=8;
+ ae->lz=ae->ilz;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->lzsection,&ae->ilz,&ae->mlz,&curlz,sizeof(curlz));
+}
+void __LZ48(struct s_assenv *ae) {
+ struct s_lz_section curlz;
+
+ if (!ae->wl[ae->idx].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LZ directive does not need any parameter\n");
+ return;
+ }
+ if (ae->lz>=0 && ae->lz<ae->ilz) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot start a new LZ section inside another one (%d)\n",GetCurrentFile(ae),ae->wl[ae->idx].l,ae->lz);
+ FreeAssenv(ae);
+ exit(-5);
+ }
+ curlz.iw=ae->idx;
+ curlz.iorgzone=ae->io-1;
+ curlz.ibank=ae->activebank;
+ curlz.memstart=ae->outputadr;
+ curlz.memend=-1;
+ curlz.lzversion=48;
+ ae->lz=ae->ilz;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->lzsection,&ae->ilz,&ae->mlz,&curlz,sizeof(curlz));
+}
+void __LZ49(struct s_assenv *ae) {
+ struct s_lz_section curlz;
+
+ if (!ae->wl[ae->idx].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LZ directive does not need any parameter\n");
+ return;
+ }
+ if (ae->lz>=0 && ae->lz<ae->ilz) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot start a new LZ section inside another one (%d)\n",GetCurrentFile(ae),ae->wl[ae->idx].l,ae->lz);
+ FreeAssenv(ae);
+ exit(-5);
+ }
+
+ curlz.iw=ae->idx;
+ curlz.iorgzone=ae->io-1;
+ curlz.ibank=ae->activebank;
+ curlz.memstart=ae->outputadr;
+ curlz.memend=-1;
+ curlz.lzversion=49;
+ ae->lz=ae->ilz;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->lzsection,&ae->ilz,&ae->mlz,&curlz,sizeof(curlz));
+}
+void __LZCLOSE(struct s_assenv *ae) {
+ if (!ae->ilz || ae->lz==-1) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot close LZ section as it wasn't opened\n");
+ return;
+ }
+
+ ae->lzsection[ae->ilz-1].memend=ae->outputadr;
+ ae->lzsection[ae->ilz-1].ilabel=ae->il;
+ ae->lzsection[ae->ilz-1].iexpr=ae->ie;
+ //ae->lz=ae->ilz;
+ ae->lz=-1;
+}
+
+void __LIMIT(struct s_assenv *ae) {
+ if (ae->wl[ae->idx+1].t!=2) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ ___output_set_limit(ae,RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->outputadr,0,0));
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LIMIT directive need one integer parameter\n");
+ }
+}
+void OverWriteCheck(struct s_assenv *ae)
+{
+ #undef FUNC
+ #define FUNC "OverWriteCheck"
+
+ int i,j;
+
+ /* overwrite checking */
+ i=ae->io-1; {
+ if (ae->orgzone[i].memstart!=ae->orgzone[i].memend) {
+ for (j=0;j<ae->io-1;j++) {
+ if (ae->orgzone[j].memstart!=ae->orgzone[j].memend && !ae->orgzone[j].nocode) {
+ if (ae->orgzone[i].ibank==ae->orgzone[j].ibank) {
+ if ((ae->orgzone[i].memstart>=ae->orgzone[j].memstart && ae->orgzone[i].memstart<ae->orgzone[j].memend)
+ || (ae->orgzone[i].memend>ae->orgzone[j].memstart && ae->orgzone[i].memend<ae->orgzone[j].memend)
+ || (ae->orgzone[i].memstart<=ae->orgzone[j].memstart && ae->orgzone[i].memend>=ae->orgzone[j].memend)) {
+ ae->idx--;
+ if (ae->orgzone[j].protect) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"PROTECTED section error [%s] L%d [#%04X-#%04X-B%d] with [%s] L%d [#%04X/#%04X]\n",ae->filename[ae->orgzone[j].ifile],ae->orgzone[j].iline,ae->orgzone[j].memstart,ae->orgzone[j].memend,ae->orgzone[j].ibank<32?ae->orgzone[j].ibank:0,ae->filename[ae->orgzone[i].ifile],ae->orgzone[i].iline,ae->orgzone[i].memstart,ae->orgzone[i].memend);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Assembling overwrite [%s] L%d [#%04X-#%04X-B%d] with [%s] L%d [#%04X/#%04X]\n",ae->filename[ae->orgzone[j].ifile],ae->orgzone[j].iline,ae->orgzone[j].memstart,ae->orgzone[j].memend,ae->orgzone[j].ibank<32?ae->orgzone[j].ibank:0,ae->filename[ae->orgzone[i].ifile],ae->orgzone[i].iline,ae->orgzone[i].memstart,ae->orgzone[i].memend);
+ }
+ i=j=ae->io;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void ___new_memory_space(struct s_assenv *ae)
+{
+ #undef FUNC
+ #define FUNC "___new_memory_space"
+
+ unsigned char *mem;
+ struct s_orgzone orgzone={0};
+
+ __internal_UpdateLZBlockIfAny(ae);
+ if (ae->io) {
+ ae->orgzone[ae->io-1].memend=ae->outputadr;
+ }
+ if (ae->lz>=0) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: LZ section wasn't closed before a new memory space directive\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ __LZCLOSE(ae);
+ }
+ ae->activebank=ae->nbbank;
+ mem=MemMalloc(65536);
+ memset(mem,0,65536);
+ ObjectArrayAddDynamicValueConcat((void**)&ae->mem,&ae->nbbank,&ae->maxbank,&mem,sizeof(mem));
+
+ ae->outputadr=0;
+ ae->codeadr=0;
+ orgzone.memstart=0;
+ orgzone.ibank=ae->activebank;
+ orgzone.nocode=ae->nocode=0;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->orgzone,&ae->io,&ae->mo,&orgzone,sizeof(orgzone));
+
+ OverWriteCheck(ae);
+}
+
+void __BANK(struct s_assenv *ae) {
+ struct s_orgzone orgzone={0};
+ int oldcode=0,oldoutput=0;
+ int i;
+ __internal_UpdateLZBlockIfAny(ae);
+
+ if (ae->io) {
+ ae->orgzone[ae->io-1].memend=ae->outputadr;
+ }
+ /* without parameter, create a new empty space */
+ if (ae->wl[ae->idx].t==1) {
+ ___new_memory_space(ae);
+ return;
+ }
+
+ ae->bankmode=1;
+ if (!ae->forceROM && !ae->forcecpr && !ae->forcesnapshot && !ae->forcezx) ae->forcecpr=1;
+
+ if (ae->wl[ae->idx+1].t!=2) {
+ if (strcmp(ae->wl[ae->idx+1].w,"NEXT")==0) {
+ /* are we in a temporary space or in the very last bank? */
+ if (ae->activebank>=260-1) {
+ ___new_memory_space(ae);
+ return;
+ }
+ /* switch to next bank! */
+ ae->activebank++;
+ } else {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ ae->activebank=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0);
+ }
+ if (ae->forcecpr && (ae->activebank<0 || ae->activebank>31)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"FATAL - Bank selection must be from 0 to 31 in cartridge mode\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ FreeAssenv(ae);
+ exit(2);
+ } else if (ae->forcezx && (ae->activebank<0 || ae->activebank>7)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"FATAL - Bank selection must be from 0 to 7 in ZX Spectrum mode\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ FreeAssenv(ae);
+ exit(2);
+ } else if (ae->forceROM && (ae->activebank<0 || ae->activebank>=256)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"FATAL - Bank selection must be from 0 to 255 in ROM mode\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ FreeAssenv(ae);
+ exit(2);
+ } else if (ae->forcesnapshot && (ae->activebank<0 || ae->activebank>=260)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"FATAL - Bank selection must be from 0 to 259 in snapshot mode\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ FreeAssenv(ae);
+ exit(2);
+ }
+ /* bankset control */
+ if (ae->forcesnapshot && ae->bankset[ae->activebank/4]) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot BANK %d was already select by a previous BANKSET %d\n",ae->activebank,(int)ae->activebank/4);
+ ae->idx++;
+ return;
+ } else {
+ ae->bankused[ae->activebank]=1;
+ }
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BANK directive need one integer parameter\n");
+ return;
+ }
+ if (ae->lz>=0) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: LZ section wasn't closed before a new BANK directive\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ __LZCLOSE(ae);
+ }
+
+ /* try to get an old ORG settings backward */
+ for (i=ae->io-1;i>=0;i--) {
+ if (ae->orgzone[i].ibank==ae->activebank) {
+ oldcode=ae->orgzone[i].memend;
+ oldoutput=ae->orgzone[i].memend;
+ break;
+ }
+ }
+ ae->outputadr=oldoutput;
+ ae->codeadr=oldcode;
+ orgzone.memstart=ae->outputadr;
+ /* legacy */
+ orgzone.ibank=ae->activebank;
+ orgzone.nocode=ae->nocode=0;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->orgzone,&ae->io,&ae->mo,&orgzone,sizeof(orgzone));
+
+ OverWriteCheck(ae);
+}
+
+void __BANKSET(struct s_assenv *ae) {
+ struct s_orgzone orgzone={0};
+ int ibank;
+
+ __internal_UpdateLZBlockIfAny(ae);
+
+ if (!ae->forcesnapshot && !ae->forcecpr && !ae->forcezx) ae->forcesnapshot=1;
+ if (!ae->forcesnapshot) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BANKSET directive is specific to snapshot output\n");
+ return;
+ }
+
+ if (ae->io) {
+ ae->orgzone[ae->io-1].memend=ae->outputadr;
+ }
+ ae->bankmode=1;
+
+ if (ae->wl[ae->idx+1].t!=2) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ ae->activebank=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0);
+ ae->activebank*=4;
+ if (ae->forcesnapshot && (ae->activebank<0 || ae->activebank>=260)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"FATAL - Bank set selection must be from 0 to 64 in snapshot mode\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ FreeAssenv(ae);
+ exit(2);
+ }
+ /* control */
+ ibank=ae->activebank;
+ if (ae->bankused[ibank] || ae->bankused[ibank+1]|| ae->bankused[ibank+2]|| ae->bankused[ibank+3]) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot BANKSET because bank %d was already selected in single page mode\n",ibank);
+ ae->idx++;
+ return;
+ } else {
+ ae->bankset[ae->activebank/4]=1; /* pas très heureux mais bon... */
+ }
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BANKSET directive need one integer parameter\n");
+ return;
+ }
+ if (ae->lz>=0) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: LZ section wasn't closed before a new BANKSET directive\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ __LZCLOSE(ae);
+ }
+
+ ae->outputadr=0;
+ ae->codeadr=0;
+ orgzone.memstart=0;
+ orgzone.ibank=ae->activebank;
+ orgzone.nocode=ae->nocode=0;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->orgzone,&ae->io,&ae->mo,&orgzone,sizeof(orgzone));
+
+ OverWriteCheck(ae);
+}
+
+
+void __NameBANK(struct s_assenv *ae) {
+ int ibank;
+
+ ae->bankmode=1;
+ if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) {
+ if (!StringIsQuote(ae->wl[ae->idx+2].w)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Syntax is NAMEBANK <bank number>,'<string>'\n");
+ } else {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ ibank=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0);
+ if (ibank<0 || ibank>=BANK_MAX_NUMBER) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"NAMEBANK selection must be from 0 to %d\n",BANK_MAX_NUMBER);
+ } else {
+ ae->iwnamebank[ibank]=ae->idx+2;
+ }
+ }
+ ae->idx+=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"NAMEBANK directive need one integer parameter and a string\n");
+ }
+}
+
+/***
+ Winape little compatibility for CPR writing!
+*/
+void __WRITE(struct s_assenv *ae) {
+ int ok=0;
+ int lower=-1,upper=-1,bank=-1;
+
+ if (!ae->wl[ae->idx].t && strcmp(ae->wl[ae->idx+1].w,"DIRECT")==0 && !ae->wl[ae->idx+1].t) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+2].w,0);
+ lower=RoundComputeExpression(ae,ae->wl[ae->idx+2].w,ae->codeadr,0,0);
+ if (!ae->wl[ae->idx+2].t) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+3].w,0);
+ upper=RoundComputeExpression(ae,ae->wl[ae->idx+3].w,ae->codeadr,0,0);
+ }
+ if (!ae->wl[ae->idx+3].t) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+4].w,0);
+ bank=RoundComputeExpression(ae,ae->wl[ae->idx+4].w,ae->codeadr,0,0);
+ }
+
+ if (ae->maxam) {
+ if (lower==65535) lower=-1;
+ if (upper==65535) upper=-1;
+ if (bank==65535) bank=-1;
+ }
+
+ if (lower!=-1) {
+ if (lower>=0 && lower<8) {
+ ae->idx+=1;
+ __BANK(ae);
+ ok=1;
+ } else {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: WRITE DIRECT lower ROM ignored (value %d out of bounds 0-7)\n",GetCurrentFile(ae),ae->wl[ae->idx].l,lower);
+ }
+ } else if (upper!=-1) {
+ if (upper>=0 && ((ae->forcecpr && upper<32) || (ae->forcesnapshot && upper<BANK_MAX_NUMBER))) {
+ ae->idx+=2;
+ __BANK(ae);
+ ok=1;
+ } else {
+ if (!ae->forcecpr && !ae->forcesnapshot) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: WRITE DIRECT select a ROM without cartridge output\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ } else {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: WRITE DIRECT upper ROM ignored (value %d out of bounds 0-31)\n",GetCurrentFile(ae),ae->wl[ae->idx].l,upper);
+ }
+ }
+ } else if (bank!=-1) {
+ /* selection de bank on ouvre un nouvel espace */
+ } else {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: meaningless WRITE DIRECT\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ }
+ }
+ while (!ae->wl[ae->idx].t) ae->idx++;
+ if (!ok) {
+ ___new_memory_space(ae);
+ }
+}
+void __CHARSET(struct s_assenv *ae) {
+ int i,s,e,v,tquote;
+
+ if (ae->wl[ae->idx].t==1) {
+ /* reinit charset */
+ for (i=0;i<256;i++)
+ ae->charset[i]=i;
+ } else if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) {
+ /* string,value | byte,value */
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+2].w,0);
+ v=RoundComputeExpression(ae,ae->wl[ae->idx+2].w,ae->codeadr,0,0);
+ if (ae->wl[ae->idx+1].w[0]=='\'' || ae->wl[ae->idx+1].w[0]=='"') {
+ tquote=ae->wl[ae->idx+1].w[0];
+ if (ae->wl[ae->idx+1].w[strlen(ae->wl[ae->idx+1].w)-1]==tquote) {
+ i=1;
+ while (ae->wl[ae->idx+1].w[i] && ae->wl[ae->idx+1].w[i]!=tquote) {
+ if (ae->wl[ae->idx+1].w[i]=='\\') i++;
+ ae->charset[(int)ae->wl[ae->idx+1].w[i]]=(unsigned char)v++;
+ i++;
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CHARSET string,value has invalid quote!\n");
+ }
+ } else {
+ i=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0);
+ if (i>=0 && i<256) {
+ ae->charset[i]=(unsigned char)v;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CHARSET byte value must be 0-255\n");
+ }
+ }
+ ae->idx+=2;
+ } else if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && !ae->wl[ae->idx+2].t && ae->wl[ae->idx+3].t==1) {
+ /* start,end,value */
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+2].w,0);
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+3].w,0);
+ s=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0);
+ e=RoundComputeExpression(ae,ae->wl[ae->idx+2].w,ae->codeadr,0,0);
+ v=RoundComputeExpression(ae,ae->wl[ae->idx+3].w,ae->codeadr,0,0);
+ ae->idx+=3;
+ if (s<=e && s>=0 && e<256) {
+ for (i=s;i<=e;i++) {
+ ae->charset[i]=(unsigned char)v++;
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CHARSET winape directive wrong interval value\n");
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CHARSET winape directive wrong parameter count\n");
+ }
+}
+
+void __MACRO(struct s_assenv *ae) {
+ struct s_macro curmacro={0};
+ char *referentfilename,*zeparam;
+ int refidx,idx,getparam=1;
+ struct s_wordlist curwl;
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t!=2) {
+ /* get the name */
+ curmacro.mnemo=ae->wl[ae->idx+1].w;
+ curmacro.crc=GetCRC(curmacro.mnemo);
+ if (ae->wl[ae->idx+1].t) {
+ getparam=0;
+ }
+ /* overload forbidden */
+ if (SearchMacro(ae,curmacro.crc,curmacro.mnemo)>=0) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Macro already defined with this name\n");
+ }
+ idx=ae->idx+2;
+ while (ae->wl[idx].t!=2 && (GetCRC(ae->wl[idx].w)!=CRC_MEND || strcmp(ae->wl[idx].w,"MEND")!=0) && (GetCRC(ae->wl[idx].w)!=CRC_ENDM || strcmp(ae->wl[idx].w,"ENDM")!=0)) {
+ if (GetCRC(ae->wl[idx].w)==CRC_MACRO || strcmp(ae->wl[idx].w,"MACRO")==0) {
+ /* inception interdite */
+ referentfilename=GetCurrentFile(ae);
+ refidx=ae->idx;
+ ae->idx=idx;
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"You cannot define a macro inside another one (MACRO %s in [%s] L%d)\n",ae->wl[refidx+1].w,referentfilename,ae->wl[refidx].l);
+ __STOP(ae);
+ }
+ if (getparam) {
+ /* on prepare les parametres au remplacement */
+ zeparam=MemMalloc(strlen(ae->wl[idx].w)+3);
+ if (ae->as80) {
+ sprintf(zeparam,"%s",ae->wl[idx].w);
+ } else {
+ sprintf(zeparam,"{%s}",ae->wl[idx].w);
+ }
+ curmacro.nbparam++;
+ curmacro.param=MemRealloc(curmacro.param,curmacro.nbparam*sizeof(char **));
+ curmacro.param[curmacro.nbparam-1]=zeparam;
+ if (ae->wl[idx].t) {
+ /* duplicate parameters without brackets MUST be an OPTION */
+ getparam=0;
+ }
+ } else {
+ /* copie la liste de mots */
+ curwl=ae->wl[idx];
+ ObjectArrayAddDynamicValueConcat((void **)&curmacro.wc,&curmacro.nbword,&curmacro.maxword,&curwl,sizeof(struct s_wordlist));
+ }
+ idx++;
+ }
+ if (ae->wl[idx].t==2) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Macro was not closed\n");
+ }
+ ObjectArrayAddDynamicValueConcat((void**)&ae->macro,&ae->imacro,&ae->mmacro,&curmacro,sizeof(curmacro));
+ /* le quicksort n'est pas optimal mais on n'est pas supposé en créer des milliers */
+ qsort(ae->macro,ae->imacro,sizeof(struct s_macro),cmpmacros);
+
+ /* ajustement des mots lus */
+ if (ae->wl[idx].t==2) idx--;
+ ae->idx=idx;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"MACRO definition need at least one parameter\n");
+ }
+}
+
+struct s_wordlist *__MACRO_EXECUTE(struct s_assenv *ae, int imacro) {
+ struct s_wordlist *cpybackup;
+ int nbparam=0,idx,i,j,idad;
+ int ifile,iline,iu,lenparam;
+ double v;
+ struct s_macro_position curmacropos={0};
+ char *zeparam=NULL,*txtparamlist;
+ int reload=0;
+
+ idx=ae->idx;
+ while (!ae->wl[idx].t) {
+ nbparam++;
+ idx++;
+ }
+
+ /* hack to secure macro without parameters with void argument */
+ if (!ae->macro[imacro].nbparam) {
+ if (nbparam) {
+ if (nbparam==1 && strcmp(ae->wl[ae->idx+1].w,"(VOID)")==0) {
+ nbparam=0;
+ reload=1;
+ }
+ } else {
+ if (ae->macrovoid) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"MACRO [%s] used without (void) and option -void used!\n",ae->macro[imacro].mnemo);
+ while (!ae->wl[ae->idx].t) {
+ ae->idx++;
+ }
+ ae->idx++;
+ }
+ }
+ }
+ /* macro must avoid extra params! */
+
+ /* cannot VOID a macro with parameters! */
+ if (ae->macro[imacro].nbparam && strcmp(ae->wl[ae->idx+1].w,"(VOID)")==0) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"MACRO [%s] has %d parameter%s\n",ae->macro[imacro].mnemo,ae->macro[imacro].nbparam,ae->macro[imacro].nbparam>1?"s":"");
+ while (!ae->wl[ae->idx].t) {
+ ae->idx++;
+ }
+ ae->idx++;
+ } else {
+ if (nbparam!=ae->macro[imacro].nbparam) {
+ lenparam=1; // macro without parameters!
+ for (i=0;i<ae->macro[imacro].nbparam;i++) {
+ lenparam+=strlen(ae->macro[imacro].param[i])+3;
+ }
+ txtparamlist=MemMalloc(lenparam);
+ txtparamlist[0]=0;
+ for (i=0;i<ae->macro[imacro].nbparam;i++) {
+ strcat(txtparamlist,ae->macro[imacro].param[i]);
+ strcat(txtparamlist," ");
+ }
+
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"MACRO [%s] was defined with %d parameter%s %s\n",ae->macro[imacro].mnemo,ae->macro[imacro].nbparam,ae->macro[imacro].nbparam>1?"s":"",txtparamlist);
+ while (!ae->wl[ae->idx].t) {
+ ae->idx++;
+ }
+ ae->idx++;
+ } else {
+ /* free macro call as we will overwrite it */
+ MemFree(ae->wl[ae->idx].w);
+ /* is there a void to free? */
+ if (reload) {
+ MemFree(ae->wl[ae->idx+1].w);
+ }
+ /* eval parameters? */
+ for (i=0;i<nbparam;i++) {
+ if (strncmp(ae->wl[ae->idx+1+i].w,"{EVAL}",6)==0) {
+ /* parametre entre chevrons, il faut l'interpreter d'abord */
+ zeparam=TxtStrDup(ae->wl[ae->idx+1+i].w+6);
+ ExpressionFastTranslate(ae,&zeparam,1);
+ v=ComputeExpressionCore(ae,zeparam,ae->codeadr,0);
+ MemFree(zeparam);
+ zeparam=MemMalloc(32);
+ snprintf(zeparam,31,"%lf",v);
+ zeparam[31]=0;
+ MemFree(ae->wl[ae->idx+1+i].w);
+ ae->wl[ae->idx+1+i].w=zeparam;
+ }
+ }
+ /* backup parameters */
+ cpybackup=MemMalloc((nbparam+1)*sizeof(struct s_wordlist));
+ for (i=0;i<nbparam;i++) {
+ cpybackup[i]=ae->wl[ae->idx+1+i];
+ }
+ /* insert macro position */
+ curmacropos.start=ae->idx;
+ curmacropos.end=ae->idx+ae->macro[imacro].nbword;
+ curmacropos.value=ae->macrocounter;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->macropos,&ae->imacropos,&ae->mmacropos,&curmacropos,sizeof(curmacropos));
+
+ /* are we in a repeat/while block? */
+ for (iu=0;iu<ae->ir;iu++) if (ae->repeat[iu].maxim<ae->imacropos) ae->repeat[iu].maxim=ae->imacropos;
+ for (iu=0;iu<ae->iw;iu++) if (ae->whilewend[iu].maxim<ae->imacropos) ae->whilewend[iu].maxim=ae->imacropos;
+
+ /* update daddy macropos */
+ for (idad=0;idad<ae->imacropos-1;idad++) {
+ if (ae->macropos[idad].end>curmacropos.start) {
+ ae->macropos[idad].end+=ae->macro[imacro].nbword-1-nbparam-reload; /* coz la macro compte un mot! */
+ }
+ }
+
+ #if 0
+ for (idad=0;idad<ae->imacropos;idad++) {
+ printf("macropos[%d]=%d -> %d\n",idad,ae->macropos[idad].start,ae->macropos[idad].end);
+ }
+ #endif
+ /* insert at macro position and replace macro+parameters */
+ if (ae->macro[imacro].nbword>1+nbparam+reload) {
+ ae->nbword+=ae->macro[imacro].nbword-1-nbparam-reload;
+ ae->wl=MemRealloc(ae->wl,ae->nbword*sizeof(struct s_wordlist));
+ } else {
+ /* si on réduit pas de realloc pour ne pas perdre de donnees */
+ ae->nbword+=ae->macro[imacro].nbword-1-nbparam-reload;
+ }
+ iline=ae->wl[ae->idx].l;
+ ifile=ae->wl[ae->idx].ifile;
+ MemMove(&ae->wl[ae->idx+ae->macro[imacro].nbword],&ae->wl[ae->idx+reload+nbparam+1],(ae->nbword-ae->idx-ae->macro[imacro].nbword)*sizeof(struct s_wordlist));
+
+ for (i=0;i<ae->macro[imacro].nbword;i++) {
+ ae->wl[i+ae->idx].w=TxtStrDup(ae->macro[imacro].wc[i].w);
+ ae->wl[i+ae->idx].l=iline;
+ ae->wl[i+ae->idx].ifile=ifile;
+ /* @@@sujet a evolution, ou double controle */
+ ae->wl[i+ae->idx].t=ae->macro[imacro].wc[i].t;
+ ae->wl[i+ae->idx].e=ae->macro[imacro].wc[i].e;
+ }
+ /* replace */
+ idx=ae->idx;
+ for (i=0;i<nbparam;i++) {
+ for (j=idx;j<idx+ae->macro[imacro].nbword;j++) {
+ /* tags in upper case for replacement in quotes */
+ if (StringIsQuote(ae->wl[j].w)) {
+ int lm,touched;
+ for (lm=touched=0;ae->wl[j].w[lm];lm++) {
+ if (ae->wl[j].w[lm]=='{') touched++; else if (ae->wl[j].w[lm]=='}') touched--; else if (touched) ae->wl[j].w[lm]=toupper(ae->wl[j].w[lm]);
+ }
+ }
+ ae->wl[j].w=TxtReplace(ae->wl[j].w,ae->macro[imacro].param[i],cpybackup[i].w,0);
+ }
+ MemFree(cpybackup[i].w);
+ }
+ MemFree(cpybackup);
+ /* macro replaced, need to rollback index */
+ //ae->idx--;
+ }
+ }
+ /* a chaque appel de macro on incremente le compteur pour les labels locaux */
+ ae->macrocounter++;
+
+ return ae->wl;
+}
+
+/*
+ ticker start, <var>
+ ticker stop, <var>
+*/
+void __TICKER(struct s_assenv *ae) {
+ struct s_expr_dico *tvar;
+ struct s_ticker ticker;
+ int crc,i;
+
+ if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) {
+ crc=GetCRC(ae->wl[ae->idx+2].w);
+
+ if (strcmp(ae->wl[ae->idx+1].w,"START")==0) {
+ /* is there already a counter? */
+ for (i=0;i<ae->iticker;i++) {
+ if (ae->ticker[i].crc==crc && strcmp(ae->wl[ae->idx+2].w,ae->ticker[i].varname)==0) {
+ break;
+ }
+ }
+ if (i==ae->iticker) {
+ ticker.varname=TxtStrDup(ae->wl[ae->idx+2].w);
+ ticker.crc=crc;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->ticker,&ae->iticker,&ae->mticker,&ticker,sizeof(struct s_ticker));
+ }
+ ae->ticker[i].nopstart=ae->nop;
+ } else if (strcmp(ae->wl[ae->idx+1].w,"STOP")==0) {
+ for (i=0;i<ae->iticker;i++) {
+ if (ae->ticker[i].crc==crc && strcmp(ae->wl[ae->idx+2].w,ae->ticker[i].varname)==0) {
+ break;
+ }
+ }
+ if (i<ae->iticker) {
+ /* set var */
+ if ((tvar=SearchDico(ae,ae->wl[ae->idx+2].w,crc))!=NULL) {
+ /* compute nop count */
+ tvar->v=ae->nop-ae->ticker[i].nopstart;
+ } else {
+ /* create var with nop count */
+ ExpressionSetDicoVar(ae,ae->wl[ae->idx+2].w,ae->nop-ae->ticker[i].nopstart);
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"TICKER not found\n");
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"usage is TICKER start/stop,<variable>\n");
+ }
+ ae->idx+=2;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"usage is TICKER start/stop,<variable>\n");
+ }
+}
+
+void __LET(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ae->idx++;
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0);
+ RoundComputeExpression(ae,ae->wl[ae->idx].w,ae->codeadr,0,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LET useless winape directive need one expression\n");
+ }
+}
+
+void __RUN(struct s_assenv *ae) {
+ int ramconf=0xC0;
+
+ if (!ae->wl[ae->idx].t) {
+ ae->current_run_idx=ae->idx+1;
+ if (ae->forcezx) {
+ if (!ae->wl[ae->idx].t) {
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_ZXRUN); // delayed RUN value
+ PushExpression(ae,ae->idx+2,E_EXPRESSION_ZXSTACK); // delayed STACK value
+ ae->idx+=2;
+ } else {
+ ae->idx++;
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"usage is RUN <adress>,<stack> (ZX mode)\n");
+ }
+ } else {
+ PushExpression(ae,ae->idx+1,E_EXPRESSION_RUN); // delayed RUN value
+ ae->idx++;
+ if (!ae->wl[ae->idx].t) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ ramconf=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0);
+ ae->idx++;
+ if (ramconf<0xC0 || ramconf>0xFF) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: ram configuration out of bound %X forced to #C0\n",GetCurrentFile(ae),ae->wl[ae->idx].l,ramconf);
+ }
+ }
+ }
+ } else {
+ if (ae->forcezx) MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"usage is RUN <adress>,<stack> (ZX mode)\n");
+ else MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"usage is RUN <adress>[,<ppi>]\n");
+ }
+ ae->snapshot.ramconfiguration=ramconf;
+ if (ae->rundefined && !ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: run adress redefinition\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ ae->rundefined=1;
+}
+void __BREAKPOINT(struct s_assenv *ae) {
+ struct s_breakpoint breakpoint={0};
+
+ if (ae->activebank>3) breakpoint.bank=1;
+ if (ae->wl[ae->idx].t) {
+ breakpoint.address=ae->codeadr;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->breakpoint,&ae->ibreakpoint,&ae->maxbreakpoint,&breakpoint,sizeof(struct s_breakpoint));
+ } else if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ breakpoint.address=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0);
+ ObjectArrayAddDynamicValueConcat((void **)&ae->breakpoint,&ae->ibreakpoint,&ae->maxbreakpoint,&breakpoint,sizeof(struct s_breakpoint));
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is BREAKPOINT [adress]\n");
+ }
+}
+void __SETCPC(struct s_assenv *ae) {
+ int mycpc;
+
+ if (!ae->forcecpr) {
+ ae->forcesnapshot=1;
+ } else {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: Cannot SETCPC when already in cartridge output\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ }
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ mycpc=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0);
+ ae->idx++;
+ switch (mycpc) {
+ case 0:
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ case 6:
+ ae->snapshot.CPCType=mycpc;
+ break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SETCPC directive has wrong value (0,1,2,4,5,6 only)\n");
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SETCPC directive need one integer parameter\n");
+ }
+}
+void __SETCRTC(struct s_assenv *ae) {
+ int mycrtc;
+
+ if (!ae->forcecpr) {
+ ae->forcesnapshot=1;
+ } else {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: Cannot SETCRTC when already in cartridge output\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ }
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ mycrtc=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0);
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SETCRTC directive need one integer parameter\n");
+ }
+ switch (mycrtc) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ ae->snapshot.crtcstate.model=mycrtc;
+ break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SETCRTC directive has wrong value (0,1,2,3,4 only)\n");
+ }
+}
+
+
+void __LIST(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LIST winape directive do not need parameter\n");
+ }
+}
+void __NOLIST(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"NOLIST winape directive do not need parameter\n");
+ }
+}
+
+void __BRK(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BRK winape directive do not need parameter\n");
+ }
+}
+
+void __STOP(struct s_assenv *ae) {
+ rasm_printf(ae,"[%s:%d] STOP assembling requested\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ while (ae->wl[ae->idx].t!=2) ae->idx++;
+ ae->idx--;
+ ae->stop=1;
+}
+
+void __PRINT(struct s_assenv *ae) {
+ while (ae->wl[ae->idx].t!=1) {
+ if (!StringIsQuote(ae->wl[ae->idx+1].w)) {
+ char *string2print=NULL;
+ int hex=0,bin=0,entier=0;
+
+ if (strncmp(ae->wl[ae->idx+1].w,"{HEX}",5)==0) {
+ string2print=TxtStrDup(ae->wl[ae->idx+1].w+5);
+ hex=1;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"{HEX2}",6)==0) {
+ string2print=TxtStrDup(ae->wl[ae->idx+1].w+6);
+ hex=2;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"{HEX4}",6)==0) {
+ string2print=TxtStrDup(ae->wl[ae->idx+1].w+6);
+ hex=4;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"{HEX8}",6)==0) {
+ string2print=TxtStrDup(ae->wl[ae->idx+1].w+6);
+ hex=8;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"{BIN}",5)==0) {
+ string2print=TxtStrDup(ae->wl[ae->idx+1].w+5);
+ bin=1;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"{BIN8}",6)==0) {
+ string2print=TxtStrDup(ae->wl[ae->idx+1].w+6);
+ bin=8;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"{BIN16}",7)==0) {
+ string2print=TxtStrDup(ae->wl[ae->idx+1].w+7);
+ bin=16;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"{BIN32}",7)==0) {
+ string2print=TxtStrDup(ae->wl[ae->idx+1].w+7);
+ bin=32;
+ } else if (strncmp(ae->wl[ae->idx+1].w,"{INT}",5)==0) {
+ string2print=TxtStrDup(ae->wl[ae->idx+1].w+5);
+ entier=1;
+ } else {
+ string2print=TxtStrDup(ae->wl[ae->idx+1].w);
+ }
+
+ ExpressionFastTranslate(ae,&string2print,1);
+ if (hex) {
+ int zv;
+ zv=RoundComputeExpressionCore(ae,string2print,ae->codeadr,0);
+ switch (hex) {
+ case 1:
+ if (zv&0xFFFFFF00) {
+ if (zv&0xFFFF0000) {
+ rasm_printf(ae,"#%-8.08X ",zv);
+ } else {
+ rasm_printf(ae,"#%-4.04X ",zv);
+ }
+ } else {
+ rasm_printf(ae,"#%-2.02X ",zv);
+ }
+ break;
+ case 2:rasm_printf(ae,"#%-2.02X ",zv);break;
+ case 4:rasm_printf(ae,"#%-4.04X ",zv);break;
+ case 8:rasm_printf(ae,"#%-8.08X ",zv);break;
+ }
+ } else if (bin) {
+ int zv,d;
+ zv=RoundComputeExpressionCore(ae,string2print,ae->codeadr,0);
+ /* remove useless sign bits */
+ if (bin<32 && (zv&0xFFFF0000)==0xFFFF0000) {
+ zv&=0xFFFF;
+ }
+ switch (bin) {
+ case 1:if (zv&0xFF00) d=15; else d=7;break;
+ case 8:d=7;break;
+ case 16:d=15;break;
+ case 32:d=31;break;
+ }
+ rasm_printf(ae,"%%");
+ for (;d>=0;d--) {
+ if ((zv>>d)&1) rasm_printf(ae,"1"); else rasm_printf(ae,"0");
+ }
+ rasm_printf(ae," ");
+ } else if (entier) {
+ rasm_printf(ae,"%d ",(int)RoundComputeExpressionCore(ae,string2print,ae->codeadr,0));
+ } else {
+ rasm_printf(ae,"%.2lf ",ComputeExpressionCore(ae,string2print,ae->codeadr,0));
+ }
+ MemFree(string2print);
+ } else {
+ char *varbuffer;
+ int lm,touched;
+ lm=strlen(ae->wl[ae->idx+1].w)-2;
+ if (lm) {
+ varbuffer=MemMalloc(lm+2);
+ sprintf(varbuffer,"%-*.*s ",lm,lm,ae->wl[ae->idx+1].w+1);
+ /* need to upper case tags */
+ for (lm=touched=0;varbuffer[lm];lm++) {
+ if (varbuffer[lm]=='{') touched++; else if (varbuffer[lm]=='}') touched--; else if (touched) varbuffer[lm]=toupper(varbuffer[lm]);
+ }
+ /* translate tag will check tag consistency */
+ varbuffer=TranslateTag(ae,varbuffer,&touched,1,E_TAGOPTION_REMOVESPACE);
+ varbuffer=TxtReplace(varbuffer,"\\b","\b",0);
+ varbuffer=TxtReplace(varbuffer,"\\v","\v",0);
+ varbuffer=TxtReplace(varbuffer,"\\f","\f",0);
+ varbuffer=TxtReplace(varbuffer,"\\r","\r",0);
+ varbuffer=TxtReplace(varbuffer,"\\n","\n",0);
+ varbuffer=TxtReplace(varbuffer,"\\t","\t",0);
+ rasm_printf(ae,"%s ",varbuffer);
+ MemFree(varbuffer);
+ }
+ }
+ ae->idx++;
+ }
+ rasm_printf(ae,"\n");
+}
+
+void __FAIL(struct s_assenv *ae) {
+ __PRINT(ae);
+ __STOP(ae);
+ MaxError(ae);
+}
+
+void __ALIGN(struct s_assenv *ae) {
+ int aval,ifill=-1;
+
+ if (ae->io) {
+ ae->orgzone[ae->io-1].memend=ae->outputadr;
+ }
+ if (!ae->wl[ae->idx].t) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ aval=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0);
+ ae->idx++;
+ /* align with fill ? */
+ if (!ae->wl[ae->idx].t) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ ifill=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0);
+ ae->idx++;
+ if (ifill<0 || ifill>255) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ALIGN fill value must be 0 to 255\n");
+ ifill=0;
+ }
+ }
+
+ if (aval<1 || aval>65535) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ALIGN boundary must be greater than zero and lower than 65536\n");
+ aval=1;
+ }
+
+ /* touch codeadr only if adress is misaligned */
+ if (ae->codeadr%aval) {
+ if (ifill==-1) {
+ /* virtual ALIGN is moving outputadr the same value as codeadr move */
+ ae->outputadr=ae->outputadr-(ae->codeadr%aval)+aval;
+ ae->codeadr=ae->codeadr-(ae->codeadr%aval)+aval;
+ } else {
+ /* physical ALIGN fill bytes */
+ while (ae->codeadr%aval) {
+ ___output(ae,ifill);
+ ae->nop+=1;
+ }
+ }
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ALIGN <boundary>[,fill] directive need one or two integers parameters\n");
+ }
+}
+
+void ___internal_skip_loop_block(struct s_assenv *ae, int eloopstyle) {
+ int *loopstyle=NULL;
+ int iloop=0,mloop=0;
+ int cidx;
+
+ cidx=ae->idx+2;
+
+ IntArrayAddDynamicValueConcat(&loopstyle,&iloop,&mloop,eloopstyle);
+ /* look for WEND */
+ while (iloop) {
+ if (strcmp(ae->wl[cidx].w,"REPEAT")==0) {
+ if (ae->wl[cidx].t) {
+ IntArrayAddDynamicValueConcat(&loopstyle,&iloop,&mloop,E_LOOPSTYLE_REPEATUNTIL);
+ } else if (ae->wl[cidx+1].t) {
+ IntArrayAddDynamicValueConcat(&loopstyle,&iloop,&mloop,E_LOOPSTYLE_REPEATN);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid REPEAT syntax\n");
+ }
+ } else if (strcmp(ae->wl[cidx].w,"WHILE")==0) {
+ if (!ae->wl[cidx].t && ae->wl[cidx+1].t) {
+ IntArrayAddDynamicValueConcat(&loopstyle,&iloop,&mloop,E_LOOPSTYLE_WHILE);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid WHILE syntax\n");
+ }
+ } else if (strcmp(ae->wl[cidx].w,"WEND")==0) {
+ iloop--;
+ if (iloop<0) {
+ iloop=0;
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WEND encountered that was not expected\n");
+ } else if (loopstyle[iloop]!=E_LOOPSTYLE_WHILE) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WEND encountered but expecting %s\n",loopstyle[iloop]==E_LOOPSTYLE_REPEATN?"REND":"UNTIL");
+ }
+ } else if (strcmp(ae->wl[cidx].w,"REND")==0) {
+ iloop--;
+ if (iloop<0) {
+ iloop=0;
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"REND encountered that was not expected\n");
+ } else if (loopstyle[iloop]!=E_LOOPSTYLE_REPEATN) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"REND encountered but expecting %s\n",loopstyle[iloop]==E_LOOPSTYLE_REPEATUNTIL?"UNTIL":"WEND");
+ }
+ } else if (strcmp(ae->wl[cidx].w,"UNTIL")==0) {
+ iloop--;
+ if (iloop<0) {
+ iloop=0;
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"UNTIL encountered that was not expected\n");
+ } else if (loopstyle[iloop]!=E_LOOPSTYLE_REPEATUNTIL) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"UNTIL encountered but expecting %s\n",loopstyle[iloop]==E_LOOPSTYLE_REPEATN?"REND":"WEND");
+ }
+ }
+ while (!ae->wl[cidx].t) cidx++;
+ cidx++;
+ }
+ MemFree(loopstyle);
+ ae->idx=cidx-1;
+}
+
+void __WHILE(struct s_assenv *ae) {
+ struct s_whilewend whilewend={0};
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ if (!ComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,2)) {
+ /* skip while block */
+ ___internal_skip_loop_block(ae,E_LOOPSTYLE_WHILE);
+ return;
+ } else {
+ ae->idx++;
+ whilewend.start=ae->idx;
+ whilewend.cpt=0;
+ whilewend.value=ae->whilecounter;
+ whilewend.maxim=ae->imacropos;
+ whilewend.while_counter=1;
+ ae->whilecounter++;
+ /* pour gérer les macros situés dans le while précedent après un repeat/while courant */
+ if (ae->iw) whilewend.maxim=ae->whilewend[ae->iw-1].maxim;
+ if (ae->ir && ae->repeat[ae->ir-1].maxim>whilewend.maxim) whilewend.maxim=ae->repeat[ae->ir-1].maxim;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->whilewend,&ae->iw,&ae->mw,&whilewend,sizeof(whilewend));
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is WHILE <expression>\n");
+ }
+}
+void __WEND(struct s_assenv *ae) {
+ if (ae->iw>0) {
+ if (ae->wl[ae->idx].t==1) {
+ if (ComputeExpression(ae,ae->wl[ae->whilewend[ae->iw-1].start].w,ae->codeadr,0,2)) {
+ if (ae->whilewend[ae->iw-1].while_counter>65536) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Bypass infinite WHILE loop\n");
+ ae->iw--;
+ /* refresh macro check index */
+ if (ae->iw) ae->imacropos=ae->whilewend[ae->iw-1].maxim;
+ } else {
+ ae->whilewend[ae->iw-1].cpt++; /* for local label */
+ ae->whilewend[ae->iw-1].while_counter++;
+ ae->idx=ae->whilewend[ae->iw-1].start;
+ /* refresh macro check index */
+ ae->imacropos=ae->whilewend[ae->iw-1].maxim;
+ }
+ } else {
+ ae->iw--;
+ /* refresh macro check index */
+ if (ae->iw) ae->imacropos=ae->whilewend[ae->iw-1].maxim;
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WEND does not need any parameter\n");
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WEND encounter whereas there is no referent WHILE\n");
+ }
+}
+
+void __REPEAT(struct s_assenv *ae) {
+ struct s_repeat currepeat={0};
+ struct s_expr_dico *rvar;
+ int *loopstyle;
+ int iloop,mloop;
+ int cidx,crc;
+
+ if (ae->wl[ae->idx+1].t!=2) {
+ if (ae->wl[ae->idx].t==0) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ currepeat.cpt=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,0,0,0);
+ if (!currepeat.cpt) {
+ /* skip repeat block */
+ ___internal_skip_loop_block(ae,E_LOOPSTYLE_REPEATN);
+ return;
+ } else if (currepeat.cpt<1 || currepeat.cpt>65536) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Repeat value (%d) must be from 1 to 65535. Skipping block\n",currepeat.cpt);
+ ___internal_skip_loop_block(ae,E_LOOPSTYLE_REPEATN);
+ return;
+ }
+ ae->idx++;
+ currepeat.start=ae->idx;
+ if (ae->wl[ae->idx].t==0) {
+ ae->idx++;
+ if (ae->wl[ae->idx].t==1) {
+ /* la variable peut exister -> OK */
+ crc=GetCRC(ae->wl[ae->idx].w);
+ if ((rvar=SearchDico(ae,ae->wl[ae->idx].w,crc))!=NULL) {
+ rvar->v=1;
+ } else {
+ /* mais ne peut être un label ou un alias */
+ ExpressionSetDicoVar(ae,ae->wl[ae->idx].w, 1);
+ }
+ currepeat.repeatvar=ae->wl[ae->idx].w;
+ currepeat.repeatcrc=crc;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"extended syntax is REPEAT <n>,<var>\n");
+ }
+ }
+ } else {
+ currepeat.start=ae->idx;
+ currepeat.cpt=-1;
+ }
+ currepeat.value=ae->repeatcounter;
+ currepeat.repeat_counter=1;
+ ae->repeatcounter++;
+ /* pour gérer les macros situés dans le repeat précedent après le repeat courant */
+ if (ae->ir) currepeat.maxim=ae->repeat[ae->ir-1].maxim;
+ if (ae->iw && ae->whilewend[ae->iw-1].maxim>currepeat.maxim) currepeat.maxim=ae->whilewend[ae->iw-1].maxim;
+ if (ae->imacropos>currepeat.maxim) currepeat.maxim=ae->imacropos;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->repeat,&ae->ir,&ae->mr,&currepeat,sizeof(currepeat));
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"wrong REPEAT usage\n");
+ }
+}
+
+void __REND(struct s_assenv *ae) {
+ struct s_expr_dico *rvar;
+ if (ae->ir>0) {
+ if (ae->repeat[ae->ir-1].cpt==-1) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"REND encounter whereas referent REPEAT was waiting for UNTIL\n");
+ } else {
+ ae->repeat[ae->ir-1].cpt--;
+ ae->repeat[ae->ir-1].repeat_counter++;
+ if ((rvar=SearchDico(ae,ae->repeat[ae->ir-1].repeatvar,ae->repeat[ae->ir-1].repeatcrc))!=NULL) {
+ rvar->v=ae->repeat[ae->ir-1].repeat_counter;
+ }
+ if (ae->repeat[ae->ir-1].cpt) {
+ ae->idx=ae->repeat[ae->ir-1].start;
+ /* refresh macro check index */
+ ae->imacropos=ae->repeat[ae->ir-1].maxim;
+ } else {
+ ae->ir--;
+ /* refresh macro check index */
+ if (ae->ir) ae->imacropos=ae->repeat[ae->ir-1].maxim;
+ }
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"REND encounter whereas there is no referent REPEAT\n");
+ }
+}
+
+void __UNTIL(struct s_assenv *ae) {
+ if (ae->ir>0) {
+ if (ae->repeat[ae->ir-1].cpt>=0) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"[%s:%d] UNTIL encounter whereas referent REPEAT n was waiting for REND\n");
+ } else {
+ if (ae->wl[ae->idx].t==0 && ae->wl[ae->idx+1].t==1) {
+ ae->repeat[ae->ir-1].repeat_counter++;
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ if (!ComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,2)) {
+ if (ae->repeat[ae->ir-1].repeat_counter>65536) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Bypass infinite REPEAT loop\n");
+ ae->ir--;
+ /* refresh macro check index */
+ if (ae->ir) ae->imacropos=ae->repeat[ae->ir-1].maxim;
+ } else {
+ ae->idx=ae->repeat[ae->ir-1].start;
+ ae->repeat[ae->ir-1].cpt--; /* for local label */
+ /* refresh macro check index */
+ ae->imacropos=ae->repeat[ae->ir-1].maxim;
+ }
+ } else {
+ ae->ir--;
+ /* refresh macro check index */
+ if (ae->ir) ae->imacropos=ae->repeat[ae->ir-1].maxim;
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"UNTIL need one expression/evaluation as parameter\n");
+ }
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"UNTIL encounter whereas there is no referent REPEAT\n");
+ }
+}
+
+void __ASSERT(struct s_assenv *ae) {
+ char Dot3[4];
+ int rexpr;
+
+ if (!ae->wl[ae->idx].t) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ if (strlen(ae->wl[ae->idx+1].w)>29) strcpy(Dot3,"..."); else strcpy(Dot3,"");
+ rexpr=!!RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,1);
+ if (!rexpr) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx+1].l,"ASSERT %.29s%s failed with ",ae->wl[ae->idx+1].w,Dot3);
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,1);
+ rasm_printf(ae,"%s\n",ae->wl[ae->idx+1].w);
+ if (!ae->wl[ae->idx+1].t) {
+ ae->idx++;
+ rasm_printf(ae,"-> ");
+ __PRINT(ae);
+ }
+ __STOP(ae);
+ } else {
+ while (!ae->wl[ae->idx].t) ae->idx++;
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ASSERT need one expression\n");
+ }
+}
+
+void __IF(struct s_assenv *ae) {
+ struct s_ifthen ifthen={0};
+ int rexpr;
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ rexpr=!!RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,1);
+ ifthen.v=rexpr;
+ ifthen.filename=GetCurrentFile(ae);
+ ifthen.line=ae->wl[ae->idx].l;
+ ifthen.type=E_IFTHEN_TYPE_IF;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen));
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IF need one expression\n");
+ }
+}
+
+void __IF_light(struct s_assenv *ae) {
+ struct s_ifthen ifthen={0};
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ /* do not need to compute the value in shadow execution */
+ ifthen.v=0;
+ ifthen.filename=GetCurrentFile(ae);
+ ifthen.line=ae->wl[ae->idx].l;
+ ifthen.type=E_IFTHEN_TYPE_IF;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen));
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IF need one expression\n");
+ }
+}
+
+/* test if a label or a variable where used before */
+void __IFUSED(struct s_assenv *ae) {
+ struct s_ifthen ifthen={0};
+ int rexpr,crc;
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ crc=GetCRC(ae->wl[ae->idx+1].w);
+ if ((SearchDico(ae,ae->wl[ae->idx+1].w,crc))!=NULL) {
+ rexpr=1;
+ } else {
+ if ((SearchLabel(ae,ae->wl[ae->idx+1].w,crc))!=NULL) {
+ rexpr=1;
+ } else {
+ if ((SearchAlias(ae,crc,ae->wl[ae->idx+1].w))!=-1) {
+ rexpr=1;
+ } else {
+ rexpr=SearchUsed(ae,ae->wl[ae->idx+1].w,crc);
+ }
+ }
+ }
+ ifthen.v=rexpr;
+ ifthen.filename=GetCurrentFile(ae);
+ ifthen.line=ae->wl[ae->idx].l;
+ ifthen.type=E_IFTHEN_TYPE_IFUSED;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen));
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IFUSED need one variable or label\n");
+ }
+}
+void __IFNUSED(struct s_assenv *ae) {
+ __IFUSED(ae);
+ ae->ifthen[ae->ii-1].v=1-ae->ifthen[ae->ii-1].v;
+ ae->ifthen[ae->ii-1].type=E_IFTHEN_TYPE_IFNUSED;
+}
+void __IFUSED_light(struct s_assenv *ae) {
+ struct s_ifthen ifthen={0};
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ifthen.v=0;
+ ifthen.filename=GetCurrentFile(ae);
+ ifthen.line=ae->wl[ae->idx].l;
+ ifthen.type=E_IFTHEN_TYPE_IFUSED;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen));
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IFUSED need one variable or label\n");
+ }
+}
+void __IFNUSED_light(struct s_assenv *ae) {
+ __IFUSED_light(ae);
+ ae->ifthen[ae->ii-1].type=E_IFTHEN_TYPE_IFNUSED;
+}
+
+/* test if a label or a variable exists */
+void __IFDEF(struct s_assenv *ae) {
+ struct s_ifthen ifthen={0};
+ int rexpr,crc;
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ crc=GetCRC(ae->wl[ae->idx+1].w);
+ if ((SearchDico(ae,ae->wl[ae->idx+1].w,crc))!=NULL) {
+ rexpr=1;
+ } else {
+ if ((SearchLabel(ae,ae->wl[ae->idx+1].w,crc))!=NULL) {
+ rexpr=1;
+ } else {
+ if ((SearchAlias(ae,crc,ae->wl[ae->idx+1].w))!=-1) {
+ rexpr=1;
+ } else {
+ if (SearchMacro(ae,crc,ae->wl[ae->idx+1].w)>=0) {
+ rexpr=1;
+ } else {
+ rexpr=0;
+ }
+ }
+ }
+ }
+ ifthen.v=rexpr;
+ ifthen.filename=GetCurrentFile(ae);
+ ifthen.line=ae->wl[ae->idx].l;
+ ifthen.type=E_IFTHEN_TYPE_IFDEF;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen));
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IFDEF need one variable or label\n");
+ }
+}
+void __IFDEF_light(struct s_assenv *ae) {
+ struct s_ifthen ifthen={0};
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ifthen.v=0;
+ ifthen.filename=GetCurrentFile(ae);
+ ifthen.line=ae->wl[ae->idx].l;
+ ifthen.type=E_IFTHEN_TYPE_IFDEF;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen));
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"GetCurrentFile(ae),ae->wl[ae->idx].l[%s:%d] FATAL - IFDEF need one variable or label\n");
+ }
+}
+void __IFNDEF(struct s_assenv *ae) {
+ struct s_expr_dico *curdic=NULL;
+ struct s_label *curlabel=NULL;
+ struct s_ifthen ifthen={0};
+ int rexpr,crc;
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ crc=GetCRC(ae->wl[ae->idx+1].w);
+ if ((SearchDico(ae,ae->wl[ae->idx+1].w,crc))!=NULL) {
+ rexpr=0;
+ } else {
+ if ((SearchLabel(ae,ae->wl[ae->idx+1].w,crc))!=NULL) {
+ rexpr=0;
+ } else {
+ if ((SearchAlias(ae,crc,ae->wl[ae->idx+1].w))!=-1) {
+ rexpr=0;
+ } else {
+ if (SearchMacro(ae,crc,ae->wl[ae->idx+1].w)>=0) {
+ rexpr=0;
+ } else {
+ rexpr=1;
+ }
+ }
+ }
+ }
+ ifthen.v=rexpr;
+ ifthen.filename=GetCurrentFile(ae);
+ ifthen.line=ae->wl[ae->idx].l;
+ ifthen.type=E_IFTHEN_TYPE_IFNDEF;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen));
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IFNDEF need one variable or label\n");
+ }
+}
+void __IFNDEF_light(struct s_assenv *ae) {
+ struct s_expr_dico *curdic=NULL;
+ struct s_label *curlabel=NULL;
+ struct s_ifthen ifthen={0};
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ifthen.v=0;
+ ifthen.filename=GetCurrentFile(ae);
+ ifthen.line=ae->wl[ae->idx].l;
+ ifthen.type=E_IFTHEN_TYPE_IFNDEF;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen));
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IFNDEF need one variable or label\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ }
+}
+
+void __UNDEF(struct s_assenv *ae) {
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ /* no error when the variable to UNDEF does not exist */
+ DelDico(ae,ae->wl[ae->idx+1].w,GetCRC(ae->wl[ae->idx+1].w));
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is UNDEF <variable>\n");
+ }
+
+}
+
+
+void __SWITCH(struct s_assenv *ae) {
+ struct s_switchcase curswitch={0};
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ /* switch store the value */
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ curswitch.refval=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,1);
+ ObjectArrayAddDynamicValueConcat((void**)&ae->switchcase,&ae->isw,&ae->msw,&curswitch,sizeof(curswitch));
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SWITCH need one expression\n");
+ }
+}
+void __CASE(struct s_assenv *ae) {
+ int rexpr;
+
+ if (ae->isw) {
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ rexpr=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,1);
+
+ if (ae->switchcase[ae->isw-1].refval==rexpr) {
+ ae->switchcase[ae->isw-1].execute=1;
+ ae->switchcase[ae->isw-1].casematch=1;
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CASE not need one parameter\n");
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CASE encounter whereas there is no referent SWITCH\n");
+ }
+}
+void __DEFAULT(struct s_assenv *ae) {
+
+ if (ae->isw) {
+ if (ae->wl[ae->idx].t==1) {
+ /* aucun match avant, on active, sinon on laisse tel quel */
+ if (!ae->switchcase[ae->isw-1].casematch) {
+ ae->switchcase[ae->isw-1].execute=1;
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFAULT do not need parameter\n");
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFAULT encounter whereas there is no referent SWITCH\n");
+ }
+}
+void __BREAK(struct s_assenv *ae) {
+
+ if (ae->isw) {
+ if (ae->wl[ae->idx].t==1) {
+ ae->switchcase[ae->isw-1].execute=0;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BREAK do not need parameter\n");
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BREAK encounter whereas there is no referent SWITCH\n");
+ }
+}
+void __SWITCH_light(struct s_assenv *ae) {
+ struct s_switchcase curswitch={0};
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ /* shadow execution */
+ curswitch.refval=0;
+ curswitch.execute=0;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->switchcase,&ae->isw,&ae->msw,&curswitch,sizeof(curswitch));
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SWITCH need one expression\n");
+ }
+}
+void __CASE_light(struct s_assenv *ae) {
+ if (ae->isw) {
+ /* shadowed execution */
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CASE encounter whereas there is no referent SWITCH\n");
+ }
+}
+void __DEFAULT_light(struct s_assenv *ae) {
+
+ if (ae->isw) {
+ /* shadowed execution */
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFAULT encounter whereas there is no referent SWITCH\n");
+ }
+}
+void __BREAK_light(struct s_assenv *ae) {
+ if (ae->isw) {
+ /* shadowed execution */
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BREAK encounter whereas there is no referent SWITCH\n");
+ }
+}
+void __ENDSWITCH(struct s_assenv *ae) {
+ if (ae->isw) {
+ if (ae->wl[ae->idx].t==1) {
+ ae->isw--;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ENDSWITCH does not need any parameter\n");
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ENDSWITCH encounter whereas there is no referent SWITCH\n");
+ }
+}
+
+void __IFNOT(struct s_assenv *ae) {
+ struct s_ifthen ifthen={0};
+ int rexpr;
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ rexpr=!RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,1);
+ ifthen.v=rexpr;
+ ifthen.filename=GetCurrentFile(ae);
+ ifthen.line=ae->wl[ae->idx].l;
+ ifthen.type=E_IFTHEN_TYPE_IFNOT;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen));
+ //IntArrayAddDynamicValueConcat(&ae->ifthen,&ae->ii,&ae->mi,rexpr);
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IFNOT need one expression\n");
+ }
+}
+void __IFNOT_light(struct s_assenv *ae) {
+ struct s_ifthen ifthen={0};
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ifthen.v=0;
+ ifthen.filename=GetCurrentFile(ae);
+ ifthen.line=ae->wl[ae->idx].l;
+ ifthen.type=E_IFTHEN_TYPE_IFNOT;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen));
+ //IntArrayAddDynamicValueConcat(&ae->ifthen,&ae->ii,&ae->mi,rexpr);
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IFNOT need one expression\n");
+ }
+}
+
+void __ELSE(struct s_assenv *ae) {
+ if (ae->ii) {
+ if (ae->wl[ae->idx].t==1) {
+ /* ELSE a executer seulement si celui d'avant est a zero */
+ switch (ae->ifthen[ae->ii-1].v) {
+ case -1:break;
+ case 0:ae->ifthen[ae->ii-1].v=1;break;
+ case 1:ae->ifthen[ae->ii-1].v=0;break;
+ }
+ ae->ifthen[ae->ii-1].type=E_IFTHEN_TYPE_ELSE;
+ ae->ifthen[ae->ii-1].line=ae->wl[ae->idx].l;
+ ae->ifthen[ae->ii-1].filename=GetCurrentFile(ae);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ELSE does not need any parameter\n");
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ELSE encounter whereas there is no referent IF\n");
+ }
+}
+void __ELSEIF(struct s_assenv *ae) {
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ae->ifthen[ae->ii-1].type=E_IFTHEN_TYPE_ELSEIF;
+ ae->ifthen[ae->ii-1].line=ae->wl[ae->idx].l;
+ ae->ifthen[ae->ii-1].filename=GetCurrentFile(ae);
+ if (ae->ifthen[ae->ii-1].v) {
+ /* il faut signifier aux suivants qu'on va jusqu'au ENDIF */
+ ae->ifthen[ae->ii-1].v=-1;
+ } else {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ ae->ifthen[ae->ii-1].v=!!RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,1);
+ }
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ELSEIF need one expression\n");
+ }
+}
+void __ELSEIF_light(struct s_assenv *ae) {
+
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ ae->ifthen[ae->ii-1].type=E_IFTHEN_TYPE_ELSEIF;
+ ae->ifthen[ae->ii-1].line=ae->wl[ae->idx].l;
+ ae->ifthen[ae->ii-1].filename=GetCurrentFile(ae);
+ if (ae->ifthen[ae->ii-1].v) {
+ /* il faut signifier aux suivants qu'on va jusqu'au ENDIF */
+ ae->ifthen[ae->ii-1].v=-1;
+ } else {
+ ae->ifthen[ae->ii-1].v=0;
+ }
+ ae->idx++;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ELSEIF need one expression\n");
+ }
+}
+void __ENDIF(struct s_assenv *ae) {
+ if (ae->ii) {
+ if (ae->wl[ae->idx].t==1) {
+ ae->ii--;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ENDIF does not need any parameter\n");
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ENDIF encounter whereas there is no referent IF\n");
+ }
+}
+
+void __internal_PROTECT(struct s_assenv *ae, int memstart, int memend) {
+ struct s_orgzone orgzone={0};
+
+ /* add a fake ORG zone */
+ ObjectArrayAddDynamicValueConcat((void**)&ae->orgzone,&ae->io,&ae->mo,&orgzone,sizeof(orgzone));
+ /* then switch it with the current ORG */
+ orgzone=ae->orgzone[ae->io-2];
+ ae->orgzone[ae->io-2].memstart=memstart;
+ ae->orgzone[ae->io-2].memend=memend;
+ ae->orgzone[ae->io-2].ibank=ae->activebank;
+ ae->orgzone[ae->io-2].protect=1;
+ ae->orgzone[ae->io-1]=orgzone;
+}
+
+void __PROTECT(struct s_assenv *ae) {
+ int memstart,memend;
+
+ if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+2].w,0);
+ memstart=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,0,0,0);
+ memend=RoundComputeExpression(ae,ae->wl[ae->idx+2].w,0,0,0);
+ __internal_PROTECT(ae,memstart,memend);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"PROTECT need two parameters: startadr,endadr\n");
+ }
+}
+
+void ___org_close(struct s_assenv *ae) {
+ if (ae->lz>=0) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot ORG inside a LZ section\n");
+ return;
+ }
+ __internal_UpdateLZBlockIfAny(ae);
+ /* close current ORG */
+ if (ae->io) {
+ ae->orgzone[ae->io-1].memend=ae->outputadr;
+ }
+}
+
+void ___org_new(struct s_assenv *ae, int nocode) {
+ struct s_orgzone orgzone={0};
+ int i;
+
+ /* check current ORG request */
+ for (i=0;i<ae->io;i++) {
+ /* aucun contrôle sur les ORG non écrits ou en NOCODE */
+ if (ae->orgzone[i].memstart!=ae->orgzone[i].memend && !ae->orgzone[i].nocode) {
+ if (ae->orgzone[i].ibank==ae->activebank) {
+ if (ae->outputadr<ae->orgzone[i].memend && ae->outputadr>=ae->orgzone[i].memstart) {
+ if (ae->orgzone[i].protect) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ORG located a PROTECTED section [#%04X-#%04X-B%d] file [%s] line %d\n",ae->orgzone[i].memstart,ae->orgzone[i].memend,ae->orgzone[i].ibank<32?ae->orgzone[i].ibank:0,ae->filename[ae->orgzone[i].ifile],ae->orgzone[i].iline);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ORG (output at #%04X) located in a previous ORG section [#%04X-#%04X-B%d] file [%s] line %d\n",ae->outputadr,ae->orgzone[i].memstart,ae->orgzone[i].memend,ae->orgzone[i].ibank<32?ae->orgzone[i].ibank:0,ae->filename[ae->orgzone[i].ifile],ae->orgzone[i].iline);
+ }
+ }
+ }
+ }
+ }
+
+ OverWriteCheck(ae);
+ /* if there was a crunch block before, now closed */
+ if (ae->lz>=0) {
+ ae->lz=-1;
+ }
+ orgzone.memstart=ae->outputadr;
+ orgzone.ibank=ae->activebank;
+ orgzone.ifile=ae->wl[ae->idx].ifile;
+ orgzone.iline=ae->wl[ae->idx].l;
+ orgzone.nocode=ae->nocode=nocode;
+
+ if (nocode) {
+ ___output=___internal_output_nocode;
+ } else {
+ ___output=___internal_output;
+ }
+
+ ObjectArrayAddDynamicValueConcat((void**)&ae->orgzone,&ae->io,&ae->mo,&orgzone,sizeof(orgzone));
+}
+
+void __ORG(struct s_assenv *ae) {
+ ___org_close(ae);
+
+ if (ae->wl[ae->idx+1].t!=2) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0);
+ ae->codeadr=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->outputadr,0,0);
+ if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+2].w,0);
+ ae->outputadr=RoundComputeExpression(ae,ae->wl[ae->idx+2].w,ae->outputadr,0,0);
+ ae->idx+=2;
+ } else {
+ ae->outputadr=ae->codeadr;
+ ae->idx++;
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"[%s:%d] ORG code location[,output location]\n");
+ return;
+ }
+
+ ___org_new(ae,ae->nocode);
+}
+void __NOCODE(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t==1) {
+ ___org_close(ae);
+ ae->codeadrbackup=ae->codeadr;
+ ae->outputadrbackup=ae->outputadr;
+ ___org_new(ae,1);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"NOCODE directive does not need parameter\n");
+ }
+}
+void __CODE(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t) {
+ if (strcmp(ae->wl[ae->idx+1].w,"SKIP")==0) {
+ ___org_close(ae);
+ ae->codeadr=ae->codeadrbackup;
+ ae->outputadr=ae->outputadrbackup;
+ ___org_new(ae,1);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"unknown parameter for CODE directive\n");
+ }
+ ae->idx++;
+ } else if (ae->wl[ae->idx].t==1) {
+ ___org_close(ae);
+ ___org_new(ae,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CODE directive does not need parameter\n");
+ }
+}
+void __STRUCT(struct s_assenv *ae) {
+ #undef FUNC
+ #define FUNC "__STRUCT"
+ struct s_rasmstructfield rasmstructfield={0};
+ struct s_rasmstruct rasmstruct={0};
+ struct s_rasmstruct rasmstructalias={0};
+ struct s_label curlabel={0};
+ int crc,i,j,irs;
+ /* filler */
+ int localsize,cursize;
+ double zeval;
+
+ if (!ae->wl[ae->idx].t) {
+ if (ae->wl[ae->idx+1].t) {
+ /**************************************************
+ s t r u c t u r e d e c l a r a t i o n
+ **************************************************/
+ if (!ae->getstruct) {
+ /* cannot be an existing label or EQU (but variable ok) */
+ crc=GetCRC(ae->wl[ae->idx+1].w);
+ if ((SearchLabel(ae,ae->wl[ae->idx+1].w,crc))!=NULL || (SearchAlias(ae,crc,ae->wl[ae->idx+1].w))!=-1) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"STRUCT name must be different from existing labels ou aliases\n");
+ } else {
+ ae->backup_filename=GetCurrentFile(ae);
+ ae->backup_line=ae->wl[ae->idx].l;
+ ae->backup_outputadr=ae->outputadr;
+ ae->backup_codeadr=ae->codeadr;
+ ae->getstruct=1;
+ /* STRUCT = NOCODE + ORG 0 */
+ ___org_close(ae);
+ ae->codeadr=0;
+ ___org_new(ae,1);
+ /* create struct */
+ rasmstruct.name=TxtStrDup(ae->wl[ae->idx+1].w);
+ rasmstruct.crc=GetCRC(rasmstruct.name);
+ ObjectArrayAddDynamicValueConcat((void **)&ae->rasmstruct,&ae->irasmstruct,&ae->mrasmstruct,&rasmstruct,sizeof(rasmstruct));
+ ae->idx++;
+
+ /* wrapper for data capture */
+ instruction[ICRC_DEFB].makemnemo=_DEFB_struct;instruction[ICRC_DB].makemnemo=_DEFB_struct;
+ instruction[ICRC_DEFW].makemnemo=_DEFW_struct;instruction[ICRC_DW].makemnemo=_DEFW_struct;
+ instruction[ICRC_DEFI].makemnemo=_DEFI_struct;
+ instruction[ICRC_DEFR].makemnemo=_DEFR_struct;instruction[ICRC_DR].makemnemo=_DEFR_struct;
+ instruction[ICRC_DEFS].makemnemo=_DEFS_struct;instruction[ICRC_DS].makemnemo=_DEFS_struct;
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"STRUCT cannot be declared inside previous opened STRUCT [%s] Line %d\n",ae->backup_filename,ae->backup_line);
+ }
+ } else {
+ /**************************************************
+ s t r u c t u r e i n s e r t i o n
+ **************************************************/
+ int nbelem=1;
+#if TRACE_STRUCT
+printf("structure insertion\n");
+#endif
+ /* insert struct param1 in memory with name param2 */
+ crc=GetCRC(ae->wl[ae->idx+1].w);
+ /* look for existing struct */
+ for (irs=0;irs<ae->irasmstruct;irs++) {
+ if (ae->rasmstruct[irs].crc==crc && strcmp(ae->rasmstruct[irs].name,ae->wl[ae->idx+1].w)==0) break;
+ }
+ if (irs==ae->irasmstruct) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Unknown STRUCT %s\n",ae->wl[ae->idx+1].w);
+ } else {
+ /* create alias for sizeof */
+ if (!ae->getstruct) {
+ if (ae->wl[ae->idx+2].w[0]=='@') {
+ rasmstructalias.name=MakeLocalLabel(ae,ae->wl[ae->idx+2].w,NULL);
+ } else {
+ rasmstructalias.name=TxtStrDup(ae->wl[ae->idx+2].w);
+ }
+ } else {
+#if TRACE_STRUCT
+printf("struct [%s] inside struct\n",ae->wl[ae->idx+2].w);
+#endif
+ /* struct inside struct */
+ rasmstructalias.name=MemMalloc(strlen(ae->rasmstruct[ae->irasmstruct-1].name)+2+strlen(ae->wl[ae->idx+2].w));
+ sprintf(rasmstructalias.name,"%s.%s",ae->rasmstruct[ae->irasmstruct-1].name,ae->wl[ae->idx+2].w);
+ }
+ rasmstructalias.crc=GetCRC(rasmstructalias.name);
+ rasmstructalias.size=ae->rasmstruct[irs].size;
+ rasmstructalias.ptr=ae->codeadr;
+#if TRACE_STRUCT
+printf("structalias [%s] ptr=%d size=%d\n",rasmstructalias.name,rasmstructalias.ptr,rasmstructalias.size);
+#endif
+ /* extra parameter to declare an array? */
+ if (!ae->wl[ae->idx+2].t && !StringIsQuote(ae->wl[ae->idx+3].w)) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+3].w,0);
+ nbelem=RoundComputeExpression(ae,ae->wl[ae->idx+3].w,ae->outputadr,0,0);
+ if (nbelem<1) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Struct array need a positive number of elements!\n");
+ nbelem=1;
+ }
+ ae->idx++;
+ }
+ rasmstructalias.nbelem=nbelem;
+#if TRACE_STRUCT
+printf("EVOL 119 - tableau! %d elem(s)\n",nbelem);
+#endif
+ ObjectArrayAddDynamicValueConcat((void **)&ae->rasmstructalias,&ae->irasmstructalias,&ae->mrasmstructalias,&rasmstructalias,sizeof(rasmstructalias));
+
+ /* create label for global struct ptr */
+ curlabel.iw=-1;
+ curlabel.ptr=ae->codeadr;
+ if (!ae->getstruct) {
+ if (ae->wl[ae->idx+2].w[0]=='@') curlabel.name=MakeLocalLabel(ae,ae->wl[ae->idx+2].w,NULL); else curlabel.name=TxtStrDup(ae->wl[ae->idx+2].w);
+ curlabel.crc=GetCRC(curlabel.name);
+ PushLabelLight(ae,&curlabel);
+ } else {
+ /* or check for non-local name in struct declaration */
+ if (ae->wl[ae->idx+2].w[0]=='@') {
+ MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"Meaningless use of local label in a STRUCT definition\n");
+ } else {
+ curlabel.name=TxtStrDup(rasmstructalias.name);
+ curlabel.crc=GetCRC(curlabel.name);
+ PushLabelLight(ae,&curlabel);
+ }
+ }
+
+ /* first field is in fact the very beginning of the structure */
+ if (ae->getstruct) {
+ rasmstructfield.name=TxtStrDup(ae->wl[ae->idx+2].w);
+ rasmstructfield.offset=ae->codeadr;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->rasmstruct[ae->irasmstruct-1].rasmstructfield,
+ &ae->rasmstruct[ae->irasmstruct-1].irasmstructfield,&ae->rasmstruct[ae->irasmstruct-1].mrasmstructfield,
+ &rasmstructfield,sizeof(rasmstructfield));
+ }
+
+ /* create subfields */
+#if TRACE_STRUCT
+printf("create subfields\n");
+#endif
+ curlabel.iw=-1;
+ curlabel.ptr=ae->codeadr;
+ for (i=0;i<ae->rasmstruct[irs].irasmstructfield;i++) {
+ curlabel.ptr=ae->codeadr+ae->rasmstruct[irs].rasmstructfield[i].offset;
+ if (!ae->getstruct) {
+ curlabel.name=MemMalloc(strlen(ae->wl[ae->idx+2].w)+strlen(ae->rasmstruct[irs].rasmstructfield[i].name)+2);
+ sprintf(curlabel.name,"%s.%s",ae->wl[ae->idx+2].w,ae->rasmstruct[irs].rasmstructfield[i].name);
+ if (ae->wl[ae->idx+2].w[0]=='@') {
+ char *newlabel;
+ newlabel=MakeLocalLabel(ae,curlabel.name,NULL);
+ MemFree(curlabel.name);
+ curlabel.name=newlabel;
+ }
+ curlabel.crc=GetCRC(curlabel.name);
+ PushLabelLight(ae,&curlabel);
+ /* are we using a struct in a struct definition? */
+ } else {
+ /* copy structname+label+offset in the structure */
+ rasmstructfield.name=MemMalloc(strlen(ae->wl[ae->idx+2].w)+strlen(ae->rasmstruct[irs].rasmstructfield[i].name)+2);
+ sprintf(rasmstructfield.name,"%s.%s",ae->wl[ae->idx+2].w,ae->rasmstruct[irs].rasmstructfield[i].name);
+ rasmstructfield.offset=curlabel.ptr;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->rasmstruct[ae->irasmstruct-1].rasmstructfield,
+ &ae->rasmstruct[ae->irasmstruct-1].irasmstructfield,&ae->rasmstruct[ae->irasmstruct-1].mrasmstructfield,
+ &rasmstructfield,sizeof(rasmstructfield));
+
+ /* need to push also generic label */
+ curlabel.name=MemMalloc(strlen(ae->rasmstruct[ae->irasmstruct-1].name)+strlen(rasmstructfield.name)+2); /* overwrite PTR */
+ sprintf(curlabel.name,"%s.%s",ae->rasmstruct[ae->irasmstruct-1].name,rasmstructfield.name);
+ curlabel.crc=GetCRC(curlabel.name);
+ PushLabelLight(ae,&curlabel);
+ }
+ }
+
+ /* is there any filler in the declaration? */
+ localsize=0;
+
+ /* déterminer si on est en remplissage par défaut ou remplissage surchargé */
+
+
+
+
+
+#if TRACE_STRUCT
+printf("struct new behaviour (scan for %d fields)\n",ae->rasmstruct[irs].irasmstructfield);
+#endif
+#if 0
+ for (i=0;i<ae->rasmstruct[irs].irasmstructfield;i++) {
+
+ if (!ae->wl[ae->idx+2+i].t || i+1>=ae->rasmstruct[irs].irasmstructfield) {
+ /* si le champ est sur le même offset que le précédent, on le saute */
+ if (i && ae->rasmstruct[irs].rasmstructfield[i].offset>ae->rasmstruct[irs].rasmstructfield[i-1].offset) continue;
+
+#if TRACE_STRUCT
+printf("get field? (%d)\n",irs);
+#endif
+ if (!StringIsQuote(ae->wl[ae->idx+i].w)) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+i].w,1);
+ zeval=RoundComputeExpressionCore(ae,ae->wl[ae->idx+i].w,ae->codeadr,0);
+ } else {
+ // push string
+ }
+
+ localsize+=ae->rasmstruct[irs].rasmstructfield[i].size;
+
+ /* pour du single shot ?
+ pushbyte(s) at ae->codeadr+ae->rasmstruct[irs].rasmstructfield[i].offset
+ */
+
+ //ae->rasmstruct[irs].size;
+
+ } else {
+#if TRACE_STRUCT
+printf("*break*\n");
+#endif
+ break;
+ }
+ }
+#endif
+
+ /* (LEGACY) filler, on balance des zéros */
+#if TRACE_STRUCT
+printf("struct (almost) legacy filler from %d to %d-1\n",localsize,ae->rasmstruct[irs].size);
+#endif
+ while (nbelem) {
+ for (i=cursize=0;i<ae->rasmstruct[irs].irasmstructfield && cursize<localsize;i++) {
+ cursize+=ae->rasmstruct[irs].rasmstructfield[i].size;
+ }
+ for (;i<ae->rasmstruct[irs].irasmstructfield;i++) {
+ for (j=0;j<ae->rasmstruct[irs].rasmstructfield[i].idata;j++) {
+ ___output(ae,ae->rasmstruct[irs].rasmstructfield[i].data[j]);
+ }
+ }
+ nbelem--;
+ }
+
+#if 0
+ for (i=localsize;i<ae->rasmstruct[irs].size;i++) ___output(ae,0);
+#endif
+ ae->idx+=2; // probablement à revoir dans le cas d'une init!!!
+ }
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"STRUCT directive needs one or two parameters\n");
+ }
+}
+void __ENDSTRUCT(struct s_assenv *ae) {
+ #undef FUNC
+ #define FUNC "__ENDSTRUCT"
+ struct s_label curlabel={0};
+ int i,newlen;
+#if TRACE_STRUCT
+ printf("endstruct\n");
+#endif
+
+ if (!ae->wl[ae->idx].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ENDSTRUCT directive does not need parameter\n");
+ } else {
+ if (ae->getstruct) {
+ ae->rasmstruct[ae->irasmstruct-1].size=ae->codeadr;
+ ae->getstruct=0;
+
+ /* SIZEOF like Vasm with struct name */
+ curlabel.name=TxtStrDup(ae->rasmstruct[ae->irasmstruct-1].name);
+ curlabel.crc=ae->rasmstruct[ae->irasmstruct-1].crc;
+ curlabel.iw=-1;
+ curlabel.ptr=ae->rasmstruct[ae->irasmstruct-1].size;
+ PushLabelLight(ae,&curlabel);
+
+ /* compute size for each field */
+ newlen=strlen(ae->rasmstruct[ae->irasmstruct-1].name)+2;
+ for (i=0;i<ae->rasmstruct[ae->irasmstruct-1].irasmstructfield-1;i++) {
+ ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].size=ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i+1].offset-ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].offset;
+ ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].fullname=MemMalloc(newlen+strlen(ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].name));
+ sprintf(ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].fullname,"%s.%s",ae->rasmstruct[ae->irasmstruct-1].name,ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].name);
+ ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].crc=GetCRC(ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].fullname);
+ }
+ ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].size=ae->rasmstruct[ae->irasmstruct-1].size-ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].offset;
+ ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].fullname=MemMalloc(newlen+strlen(ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].name));
+ sprintf(ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].fullname,"%s.%s",ae->rasmstruct[ae->irasmstruct-1].name,ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].name);
+ ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].crc=GetCRC(ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].fullname);
+
+ /* unwrap data capture */
+ if (ae->as80==1) {/* not for UZ80 */
+ instruction[ICRC_DEFB].makemnemo=_DEFB_as80;instruction[ICRC_DB].makemnemo=_DEFB_as80;
+ instruction[ICRC_DEFW].makemnemo=_DEFW_as80;instruction[ICRC_DW].makemnemo=_DEFW_as80;
+ instruction[ICRC_DEFI].makemnemo=_DEFI_as80;
+ } else {
+ instruction[ICRC_DEFB].makemnemo=_DEFB;instruction[ICRC_DB].makemnemo=_DEFB;
+ instruction[ICRC_DEFW].makemnemo=_DEFW;instruction[ICRC_DW].makemnemo=_DEFW;
+ instruction[ICRC_DEFI].makemnemo=_DEFI;
+ }
+ instruction[ICRC_DEFR].makemnemo=_DEFR;instruction[ICRC_DR].makemnemo=_DEFR;
+ instruction[ICRC_DEFS].makemnemo=_DEFS;instruction[ICRC_DS].makemnemo=_DEFS;
+
+ /* like there was no byte */
+ ae->outputadr=ae->backup_outputadr;
+ ae->codeadr=ae->backup_codeadr;
+
+ ___org_close(ae);
+ ___org_new(ae,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ENDSTRUCT encountered outside STRUCT declaration\n");
+ }
+ }
+}
+
+void __MEMSPACE(struct s_assenv *ae) {
+ if (ae->wl[ae->idx].t) {
+ ___new_memory_space(ae);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"MEMSPACE directive does not need parameter\n");
+ }
+}
+
+int (*_internal_getsample)(unsigned char *data, int *idx);
+#undef FUNC
+#define FUNC "_internal_AudioGetSampleValue"
+
+int __internal_getsample8(unsigned char *data, int *idx) {
+ unsigned char v;
+ v=data[*idx]-128;*idx=*idx+1;return v;
+}
+int __internal_getsample16little(unsigned char *data, int *idx) {
+ int cursample;
+ cursample=data[*idx+1]-0x80;*idx=*idx+2;
+ return cursample;
+}
+int __internal_getsample24little(unsigned char *data, int *idx) {
+ int cursample;
+ cursample=data[*idx+2-0x80];*idx=*idx+3;
+ return cursample;
+}
+/* big-endian */
+int __internal_getsample16big(unsigned char *data, int *idx) {
+ int cursample;
+ cursample=data[*idx]-0x80;*idx=*idx+2;
+ return cursample;
+}
+int __internal_getsample24big(unsigned char *data, int *idx) {
+ int cursample;
+ cursample=data[*idx]-0x80;*idx=*idx+3;
+ return cursample;
+}
+/* float & endian shit */
+int _isLittleEndian() /* from lz4.h */
+{
+ const union { U32 u; BYTE c[4]; } one = { 1 };
+ return one.c[0];
+}
+
+unsigned char * __internal_floatinversion(unsigned char *data) {
+ static unsigned char bswap[4];
+ bswap[0]=data[3];
+ bswap[1]=data[2];
+ bswap[2]=data[1];
+ bswap[3]=data[0];
+ return bswap;
+}
+
+int __internal_getsample32bigbig(unsigned char *data, int *idx) {
+ float fsample;
+ int cursample;
+ fsample=*((float*)(data+*idx));
+ *idx=*idx+4;
+ cursample=(floor)((fsample+1.0)*127.5+0.5);
+ return cursample;
+}
+int __internal_getsample32biglittle(unsigned char *data, int *idx) {
+ float fsample;
+ int cursample;
+ fsample=*((float*)(__internal_floatinversion(data+*idx)));
+ *idx=*idx+4;
+ cursample=(floor)((fsample+1.0)*127.5+0.5);
+ return cursample;
+}
+
+#define __internal_getsample32littlelittle __internal_getsample32bigbig
+#define __internal_getsample32littlebig __internal_getsample32biglittle
+
+
+void _AudioLoadSample(struct s_assenv *ae, unsigned char *data, int filesize, enum e_audio_sample_type sample_type, float normalize)
+{
+ #undef FUNC
+ #define FUNC "AudioLoadSample"
+
+ struct s_wav_header *wav_header;
+ int i,j,n,idx,controlsize;
+ int nbchannel,bitspersample,nbsample;
+ int bigendian=0,cursample;
+ double accumulator;
+ unsigned char samplevalue=0, sampleprevious=0;
+ int samplerepeat=0,ipause;
+
+ unsigned char *subchunk;
+ int subchunksize;
+
+ if (filesize<sizeof(struct s_wav_header)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - this file is too small to be a valid WAV!\n");
+ return;
+ }
+
+ wav_header=(struct s_wav_header *)data;
+
+#if TRACE_HEXBIN
+printf("AudioLoadSample filesize=%d st=%d normalize=%.2lf\n",filesize,sample_type,normalize);
+#endif
+ if (strncmp(wav_header->ChunkID,"RIFF",4)) {
+ if (strncmp(wav_header->ChunkID,"RIFX",4)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - unsupported audio sample type (chunkid must be 'RIFF' or 'RIFX')\n");
+ return;
+ } else {
+ bigendian=1;
+ }
+ }
+ if (strncmp(wav_header->Format,"WAVE",4)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"[%s:%d] WAV import - unsupported audio sample type (format must be 'WAVE')\n");
+ return;
+ }
+ controlsize=wav_header->SubChunk1Size[0]+wav_header->SubChunk1Size[1]*256+wav_header->SubChunk1Size[2]*65536+wav_header->SubChunk1Size[3]*256*65536;
+ if (controlsize!=16) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - invalid wav chunk size (subchunk1 control)\n");
+ return;
+ }
+ if (strncmp(wav_header->SubChunk1ID,"fmt",3)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - unsupported audio sample type (subchunk1id must be 'fmt')\n");
+ return;
+ }
+
+#if TRACE_HEXBIN
+printf("AudioLoadSample getsubchunk\n");
+#endif
+ subchunk=(unsigned char *)&wav_header->SubChunk2ID;
+ while (strncmp(subchunk,"data",4)) {
+ subchunksize=8+subchunk[4]+subchunk[5]*256+subchunk[6]*65536+subchunk[7]*256*65536;
+ if (subchunksize>=filesize) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - data subchunk not found\n");
+ return;
+ }
+ subchunk+=subchunksize;
+ }
+ subchunksize=subchunk[4]+subchunk[5]*256+subchunk[6]*65536+subchunk[7]*256*65536;
+ controlsize=subchunksize;
+
+ nbchannel=wav_header->NumChannels[0]+wav_header->NumChannels[1]*256;
+ if (nbchannel<1) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - invalid number of audio channel\n");
+ return;
+ }
+ bitspersample=wav_header->BitsPerSample[0]+wav_header->BitsPerSample[1]*256;
+#if TRACE_HEXBIN
+printf("AudioLoadSample bitpersample=%d\n",bitspersample);
+#endif
+ switch (bitspersample) {
+ case 8:_internal_getsample=__internal_getsample8;break;
+ case 16:if (!bigendian) _internal_getsample=__internal_getsample16little; else _internal_getsample=__internal_getsample16big;break;
+ case 24:if (!bigendian) _internal_getsample=__internal_getsample24little; else _internal_getsample=__internal_getsample24big;break;
+ case 32:if (!bigendian) {
+ if (_isLittleEndian()) {
+ _internal_getsample=__internal_getsample32littlelittle;
+ } else {
+ _internal_getsample=__internal_getsample32littlebig;
+ }
+ } else {
+ if (_isLittleEndian()) {
+ _internal_getsample=__internal_getsample32biglittle;
+ } else {
+ _internal_getsample=__internal_getsample32bigbig;
+ }
+ }
+ break;
+ default:
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - unsupported bits per sample (%d)\n",bitspersample);
+ return;
+ }
+
+ nbsample=controlsize/nbchannel/(bitspersample/8);
+ if (controlsize+sizeof(struct s_wav_header)>filesize) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - cannot read %d byte%s of audio whereas the file is %d bytes big!\n",controlsize,controlsize>1?"s":"",filesize);
+ return;
+ }
+
+#if TRACE_HEXBIN
+printf("nbsample=%d (sze=%d,chn=%d,bps=%d) st=%c\n",nbsample,controlsize,nbchannel,bitspersample,sample_type);
+#endif
+
+ idx=subchunk-data;
+ switch (sample_type) {
+ default:
+ case AUDIOSAMPLE_SMP:
+ for (i=0;i<nbsample;i++) {
+ /* downmixing */
+ accumulator=0.0;
+ for (n=0;n<nbchannel;n++) {
+ accumulator+=_internal_getsample(data,&idx);
+ }
+ /* normalize */
+ cursample=MinMaxInt(floor(((accumulator/nbchannel)*normalize)+0.5)+128,0,255);
+
+ /* PSG levels */
+ samplevalue=ae->psgfine[cursample];
+
+ /* output */
+ ___output(ae,samplevalue);
+ }
+ break;
+ case AUDIOSAMPLE_SM2:
+ /* +1 pour éviter le segfault */
+ for (i=0;i<nbsample+1;i+=2) {
+ for (j=0;j<2;j++) {
+ /* downmixing */
+ accumulator=0.0;
+ for (n=0;n<nbchannel;n++) {
+ accumulator+=_internal_getsample(data,&idx);
+ }
+ /* normalize */
+ cursample=MinMaxInt(floor(((accumulator/nbchannel)*normalize)+0.5)+128,0,255);
+ /* PSG levels & packing */
+ samplevalue=(samplevalue<<4)+(ae->psgfine[cursample]);
+ }
+
+ /* output */
+ ___output(ae,samplevalue);
+ }
+ break;
+ case AUDIOSAMPLE_SM4:
+ /***
+ SM4 format has two bits
+ bits -> PSG value
+ 00 -> 0
+ 01 -> 13
+ 10 -> 14
+ 11 -> 15
+ ***/
+ /* +3 pour éviter le segfault */
+ for (i=0;i<nbsample+3;i+=4) {
+ for (j=0;j<4;j++) {
+ /* downmixing */
+ accumulator=0.0;
+ for (n=0;n<nbchannel;n++) {
+ accumulator+=_internal_getsample(data,&idx);
+ }
+ /* normalize */
+ cursample=MinMaxInt(floor(((accumulator/nbchannel)*normalize)+0.5)+128,0,255);
+ /* PSG levels & packing */
+ samplevalue=(samplevalue<<2)+(ae->psgtab[cursample]>>2);
+ }
+ /* output */
+ ___output(ae,samplevalue);
+ }
+ break;
+ case AUDIOSAMPLE_DMA:
+ sampleprevious=255;
+ for (i=0;i<nbsample;i++) {
+ /* downmixing */
+ accumulator=0.0;
+ for (n=0;n<nbchannel;n++) {
+ accumulator+=_internal_getsample(data,&idx);
+ }
+ /* normalize */
+ cursample=MinMaxInt(floor(((accumulator/nbchannel)*normalize)+0.5)+128,0,255);
+
+ /* PSG levels */
+ samplevalue=ae->psgtab[cursample];
+
+ if (samplevalue==sampleprevious) {
+ samplerepeat++;
+ } else {
+ if (!samplerepeat) {
+ /* DMA output */
+ ___output(ae,sampleprevious);
+ ___output(ae,0x0A); /* volume canal C */
+ } else {
+ /* DMA pause */
+ ___output(ae,sampleprevious);
+ ___output(ae,0x0A); /* volume canal C */
+ while (samplerepeat) {
+ ipause=samplerepeat<4096?samplerepeat:4095;
+ ___output(ae,ipause&0xFF);
+ ___output(ae,0x10 | ((ipause>>8) &0xF)); /* pause */
+
+ samplerepeat-=4096;
+ if (samplerepeat<0) samplerepeat=0;
+ }
+ }
+ sampleprevious=samplevalue;
+ }
+ }
+ if (samplerepeat) {
+ /* DMA pause */
+ ___output(ae,sampleprevious);
+ ___output(ae,0x0A); /* volume canal C */
+ while (samplerepeat) {
+ ipause=samplerepeat<4096?samplerepeat:4095;
+ ___output(ae,ipause&0xFF);
+ ___output(ae,0x10 | ((ipause>>8) &0xF)); /* pause */
+
+ samplerepeat-=4096;
+ if (samplerepeat<0) samplerepeat=0;
+ }
+ }
+ ___output(ae,0);
+ ___output(ae,0x0A); /* volume canal C */
+ ___output(ae,0x20);
+ ___output(ae,0x40); /* stop or reloop? */
+ break;
+ }
+}
+
+/*
+ meta fonction qui gère le INCBIN standard plus les variantes SMP et DMA
+*/
+void __HEXBIN(struct s_assenv *ae) {
+ int hbinidx,overwritecheck=1,crc;
+ struct s_expr_dico *rvar;
+ unsigned int idx;
+ int size=0,offset=0;
+ float amplification=1.0;
+ int deload=0;
+ int vtiles=0,remap=0,revert=0;
+ int itiles=0,tilex;
+
+ if (!ae->wl[ae->idx].t) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,1);
+ hbinidx=RoundComputeExpressionCore(ae,ae->wl[ae->idx+1].w,ae->codeadr,0);
+ if (hbinidx>ae->ih) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"internal error with binary file import (index out of bounds)\n");
+ return;
+ }
+#if TRACE_HEXBIN
+printf("Hexbin idx=[%s] filename=[%s]\n",ae->wl[ae->idx+1].w,ae->hexbin[hbinidx].filename);
+#endif
+
+ if (!ae->wl[ae->idx+1].t) {
+ if (strcmp("DSK",ae->wl[ae->idx+2].w)==0) {
+ /* import binary from DSK */
+ } else if (strchr("SD",ae->wl[ae->idx+2].w[0]) && ae->wl[ae->idx+2].w[1]=='M' &&
+ strchr("P24A",ae->wl[ae->idx+2].w[2]) && !ae->wl[ae->idx+2].w[3]) {
+ /* SMP,SM2,SM4,DMA */
+
+#if TRACE_HEXBIN
+printf("Hexbin -> %s\n",ae->wl[ae->idx+2].w);
+#endif
+ if (!ae->wl[ae->idx+2].t) {
+ amplification=ComputeExpressionCore(ae,ae->wl[ae->idx+3].w,ae->codeadr,0);
+#if TRACE_HEXBIN
+printf("sample amplification=%.2lf\n",amplification);
+#endif
+ }
+
+ switch (ae->wl[ae->idx+2].w[2]) {
+ case 'P':_AudioLoadSample(ae,ae->hexbin[hbinidx].data,ae->hexbin[hbinidx].datalen, AUDIOSAMPLE_SMP,amplification);break;
+ case '2':_AudioLoadSample(ae,ae->hexbin[hbinidx].data,ae->hexbin[hbinidx].datalen, AUDIOSAMPLE_SM2,amplification);break;
+ case '4':_AudioLoadSample(ae,ae->hexbin[hbinidx].data,ae->hexbin[hbinidx].datalen, AUDIOSAMPLE_SM4,amplification);break;
+ case 'A':_AudioLoadSample(ae,ae->hexbin[hbinidx].data,ae->hexbin[hbinidx].datalen, AUDIOSAMPLE_DMA,amplification);break;
+ default:printf("warning remover\n");break;
+ }
+ ae->idx+=2;
+ return;
+ } else {
+ /* legacy binary file */
+#if TRACE_HEXBIN
+printf("Hexbin legacy datalen=%d\n",ae->hexbin[hbinidx].datalen);
+#endif
+ if (strcmp("REVERT",ae->wl[ae->idx+2].w)==0) {
+ /* revert data */
+ if (!ae->wl[ae->idx+2].t) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN REVERT does not need extra parameters\n");
+ }
+#if TRACE_HEXBIN
+printf(" -> REVERT loading\n");
+#endif
+ revert=1;
+ offset=size=0; // full file
+ ae->idx++;
+
+ } else if (strcmp("REMAP",ae->wl[ae->idx+2].w)==0) {
+ /* reorder tiles data */
+ if (!ae->wl[ae->idx+2].t) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+3].w,1);
+ remap=RoundComputeExpressionCore(ae,ae->wl[ae->idx+3].w,ae->codeadr,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN REMAP need a number of columns for reordering\n");
+ }
+#if TRACE_HEXBIN
+printf(" -> REMAP loading\n");
+#endif
+ offset=size=0; // full file
+ ae->idx+=2;
+
+ } else if (strcmp("ITILES",ae->wl[ae->idx+2].w)==0) {
+ /*** entrelace les tiles, besoin de hauteur et largeur de la tile ***/
+ if (!ae->wl[ae->idx+2].t) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+3].w,1);
+ tilex=RoundComputeExpressionCore(ae,ae->wl[ae->idx+3].w,ae->codeadr,0);
+ itiles=1;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"usage is INCBIN'file',ITILES,width\n");
+ }
+#if TRACE_HEXBIN
+printf(" -> ITILES loading\n");
+#endif
+ offset=size=0; // full file
+ ae->idx+=2;
+ } else if (strcmp("VTILES",ae->wl[ae->idx+2].w)==0) {
+ /* import and reorder tiles */
+ if (!ae->wl[ae->idx+2].t) {
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+3].w,1);
+ vtiles=RoundComputeExpressionCore(ae,ae->wl[ae->idx+3].w,ae->codeadr,0);
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN VTILES need a number of lines for reordering\n");
+ }
+#if TRACE_HEXBIN
+printf(" -> VTILES loading\n");
+#endif
+ offset=size=0; // full file
+ ae->idx+=2;
+ } else {
+ char *expwrk;
+
+ expwrk=TxtStrDup(ae->wl[ae->idx+2].w);
+ ExpressionFastTranslate(ae,&expwrk,1);
+ offset=RoundComputeExpressionCore(ae,expwrk,ae->codeadr,0);
+ MemFree(expwrk);
+#if TRACE_HEXBIN
+ printf("offset=%d\n",offset);
+#endif
+ if (!ae->wl[ae->idx+2].t) {
+ if (ae->wl[ae->idx+3].w[0]) {
+ expwrk=TxtStrDup(ae->wl[ae->idx+3].w);
+ ExpressionFastTranslate(ae,&expwrk,1);
+ size=RoundComputeExpressionCore(ae,expwrk,ae->codeadr,0);
+ MemFree(expwrk);
+ } else {
+ size=0;
+ }
+#if TRACE_HEXBIN
+ printf("size=%d\n",size);
+#endif
+ if (size<-65535 || size>65536) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN invalid size\n");
+ }
+ if (!ae->wl[ae->idx+3].t) {
+ if (ae->wl[ae->idx+4].w[0]) {
+ expwrk=TxtStrDup(ae->wl[ae->idx+4].w);
+ ExpressionFastTranslate(ae,&expwrk,1);
+ offset+=65536*RoundComputeExpressionCore(ae,expwrk,ae->codeadr,0);
+ MemFree(expwrk);
+ }
+ if (!ae->wl[ae->idx+4].t) {
+ if (strcmp(ae->wl[ae->idx+5].w,"OFF")==0) {
+ overwritecheck=0;
+ } else if (strcmp(ae->wl[ae->idx+5].w,"ON")==0) {
+ overwritecheck=1;
+#if TRACE_HEXBIN
+ printf("mode OVERWRITE\n");
+#endif
+ } else if (ae->wl[ae->idx+5].w[0]) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN invalid overwrite value. Must be 'OFF' or 'ON'\n");
+ }
+ if (!ae->wl[ae->idx+5].t) {
+ /* copy raw len to a (new) variable */
+ crc=GetCRC(ae->wl[ae->idx+6].w);
+ if ((rvar=SearchDico(ae,ae->wl[ae->idx+6].w,crc))!=NULL) {
+ rvar->v=ae->hexbin[hbinidx].rawlen;
+ } else {
+ /* mais ne peut être un label ou un alias */
+ ExpressionSetDicoVar(ae,ae->wl[ae->idx+6].w,ae->hexbin[hbinidx].rawlen);
+ }
+ ae->idx+=6;
+ } else {
+ ae->idx+=5;
+ }
+ } else {
+ ae->idx+=4;
+ }
+ } else {
+ ae->idx+=3;
+ }
+ } else {
+ ae->idx+=2;
+ }
+ }
+ }
+ } else {
+ ae->idx++;
+ }
+
+ /* preprocessor cannot manage variables so here is the delayed load */
+ if (ae->hexbin[hbinidx].datalen<0) {
+ struct s_hexbin *curhexbin;
+ char *newfilename;
+ int lm,touched;
+ unsigned char *newdata=NULL;
+
+#if TRACE_HEXBIN
+printf("Hexbin -> as only the assembler know how to deal with var,\n");
+printf("we look for tags in the name of a file which were not found\n");
+#endif
+ curhexbin=&ae->hexbin[hbinidx];
+
+ newfilename=TxtStrDup(curhexbin->filename);
+
+ /* need to upper case tags */
+ for (lm=touched=0;newfilename[lm];lm++) {
+ if (newfilename[lm]=='{') touched++; else if (newfilename[lm]=='}') touched--; else if (touched) newfilename[lm]=toupper(newfilename[lm]);
+ }
+ /* on essaie d'interpréter le nom du fichier en dynamique */
+ newfilename=TranslateTag(ae,newfilename,&touched,1,E_TAGOPTION_REMOVESPACE);
+ /* load */
+ if (FileExists(newfilename)) {
+#if TRACE_HEXBIN
+printf("Hexbin -> surprise! we found the file!\n");
+#endif
+ curhexbin->rawlen=curhexbin->datalen=FileGetSize(newfilename);
+ curhexbin->data=MemMalloc(curhexbin->datalen*1.3+10);
+ if (FileReadBinary(newfilename,(char*)curhexbin->data,curhexbin->datalen)!=curhexbin->datalen) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"read error on file [%s]\n",newfilename);
+ return;
+ }
+ FileReadBinaryClose(newfilename);
+
+ switch (curhexbin->crunch) {
+ #ifndef NO_3RD_PARTIES
+ case 4:
+ newdata=LZ4_crunch(curhexbin->data,curhexbin->datalen,&curhexbin->datalen);
+ MemFree(curhexbin->data);
+ curhexbin->data=newdata;
+ #if TRACE_PREPRO
+ rasm_printf(ae,KVERBOSE"crunched with LZ4 into %d byte(s)\n",curhexbin->datalen);
+ #endif
+ break;
+ case 7:
+ {
+ size_t slzlen;
+ newdata=ZX7_compress(optimize(curhexbin->data, curhexbin->datalen), curhexbin->data, curhexbin->datalen, &slzlen);
+ curhexbin->datalen=slzlen;
+ MemFree(curhexbin->data);
+ curhexbin->data=newdata;
+ #if TRACE_PREPRO
+ rasm_printf(ae,KVERBOSE"crunched with ZX7 into %d byte(s)\n",curhexbin->datalen);
+ #endif
+ }
+ break;
+ case 8:
+ rasm_printf(ae,KWARNING"Exomizer is crunching %.1fkb this may take a while, be patient...\n",curhexbin->datalen/1024.0);
+ newdata=Exomizer_crunch(curhexbin->data,curhexbin->datalen,&curhexbin->datalen);
+ MemFree(curhexbin->data);
+ curhexbin->data=newdata;
+ #if TRACE_PREPRO
+ rasm_printf(ae,KVERBOSE"crunched with Exomizer into %d byte(s)\n",curhexbin->datalen);
+ #endif
+ break;
+ #endif
+ case 48:
+ newdata=LZ48_crunch(curhexbin->data,curhexbin->datalen,&curhexbin->datalen);
+ MemFree(curhexbin->data);
+ curhexbin->data=newdata;
+ #if TRACE_PREPRO
+ rasm_printf(ae,KVERBOSE"crunched with LZ48 into %d byte(s)\n",curhexbin->datalen);
+ #endif
+ break;
+ case 49:
+ newdata=LZ49_crunch(curhexbin->data,curhexbin->datalen,&curhexbin->datalen);
+ MemFree(curhexbin->data);
+ curhexbin->data=newdata;
+ #if TRACE_PREPRO
+ rasm_printf(ae,KVERBOSE"crunched with LZ49 into %d byte(s)\n",curhexbin->datalen);
+ #endif
+ break;
+ default:break;
+ }
+ deload=1;
+ } else {
+ /* still not found */
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"file not found [%s]\n",newfilename);
+ return;
+ }
+ }
+
+ if (ae->hexbin[hbinidx].datalen>0) {
+ if (hbinidx<ae->ih && hbinidx>=0) {
+ if (size<0) {
+#if TRACE_HEXBIN
+printf("taille négative %d -> conversion en %d\n",size,ae->hexbin[hbinidx].datalen+size);
+#endif
+ size=ae->hexbin[hbinidx].datalen+size;
+ if (size<1) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN negative size is greater or equal to filesize\n");
+ }
+ }
+ /* negative offset conversion */
+ if (offset<0) {
+#if TRACE_HEXBIN
+printf("offset négatif %d -> conversion en %d\n",offset,ae->hexbin[hbinidx].datalen+offset);
+#endif
+ offset=ae->hexbin[hbinidx].datalen+offset;
+ }
+ if (!size) {
+ if (!offset) {
+ size=ae->hexbin[hbinidx].datalen;
+ } else {
+ size=ae->hexbin[hbinidx].datalen-offset;
+ }
+#if TRACE_HEXBIN
+printf("taille nulle et offset=%d -> conversion en %d\n",offset,size);
+#endif
+ }
+ if (size>ae->hexbin[hbinidx].datalen) {
+ rasm_printf(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN size is greater than filesize\n");
+ } else {
+ if (size+offset>ae->hexbin[hbinidx].datalen) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN size+offset is greater than filesize\n");
+ } else {
+ if (revert) {
+ int p;
+ p=size-1;
+ while (p>=0) {
+ ___output(ae,ae->hexbin[hbinidx].data[p--]);
+ }
+ } else if (itiles) {
+ /* tiles data reordering */
+ int tx,ty,it,width;
+
+ if (size % (tilex*8)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN ITILES cannot reorder tiles %d bytewidth with file of size %d\n",tilex,size);
+ } else {
+ it=0;
+ while (it<size) {
+ for (tx=0;tx<tilex;tx++) ___output(ae,ae->hexbin[hbinidx].data[it+tx+0*tilex]);
+ for (tx=tilex-1;tx>=0;tx--) ___output(ae,ae->hexbin[hbinidx].data[it+tx+1*tilex]);
+ for (tx=0;tx<tilex;tx++) ___output(ae,ae->hexbin[hbinidx].data[it+tx+3*tilex]);
+ for (tx=tilex-1;tx>=0;tx--) ___output(ae,ae->hexbin[hbinidx].data[it+tx+2*tilex]);
+ for (tx=0;tx<tilex;tx++) ___output(ae,ae->hexbin[hbinidx].data[it+tx+6*tilex]);
+ for (tx=tilex-1;tx>=0;tx--) ___output(ae,ae->hexbin[hbinidx].data[it+tx+7*tilex]);
+ for (tx=0;tx<tilex;tx++) ___output(ae,ae->hexbin[hbinidx].data[it+tx+5*tilex]);
+ for (tx=tilex-1;tx>=0;tx--) ___output(ae,ae->hexbin[hbinidx].data[it+tx+4*tilex]);
+ it+=tilex*8;
+ }
+ }
+ } else if (remap) {
+ /* tiles data reordering */
+ int tx,ty,it,width;
+
+ width=size/remap;
+
+ if ((size % remap) || (remap*width>size)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN REMAP cannot reorder %d columns%s with file of size %d\n",remap,remap>1?"s":"",size);
+ } else {
+ for (it=0;it<remap;it++) {
+ for (tx=0;tx<width;tx++) {
+ ___output(ae,ae->hexbin[hbinidx].data[it+tx*remap]);
+ }
+ }
+ }
+
+ } else if (vtiles) {
+ /* tiles map reordering */
+ int width,tilex,tiley;
+
+ width=size/vtiles;
+
+ if ((size % vtiles) || (vtiles*width>size)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN VTILES cannot reorder %d line%s with file of size %d\n",vtiles,vtiles>1?"s":"",size);
+ } else {
+#if TRACE_HEXBIN
+printf("Hexbin -> re-tiling MAP! width=%d\n",width);
+#endif
+ for (idx=tilex=tiley=0;idx<size;idx++) {
+ ___output(ae,ae->hexbin[hbinidx].data[tilex+tiley*width]);
+ tiley++;
+ if (tiley>=vtiles) {
+ tiley=0;
+ tilex++;
+ }
+ }
+ }
+ } else {
+ /* legacy HEXBIN */
+ if (overwritecheck) {
+ for (idx=offset;idx<size+offset;idx++) {
+ ___output(ae,ae->hexbin[hbinidx].data[idx]);
+ }
+ } else {
+ ___org_close(ae);
+ ___org_new(ae,0);
+ for (idx=offset;idx<size+offset;idx++) {
+ ___output(ae,ae->hexbin[hbinidx].data[idx]);
+ }
+ /* hack to disable overwrite check */
+ ae->orgzone[ae->io-1].nocode=2;
+ ___org_close(ae);
+ ___org_new(ae,0);
+ }
+ }
+ }
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INTERNAL - HEXBIN refer to unknown structure\n");
+ FreeAssenv(ae);
+ exit(2);
+ }
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INTERNAL - HEXBIN need one HEX parameter\n");
+ FreeAssenv(ae);
+ exit(2);
+ }
+
+ /* generated names must be reloaded! */
+ if (deload) {
+ ae->hexbin[hbinidx].datalen=-1;
+ MemFree(ae->hexbin[hbinidx].data);
+ }
+}
+
+/*
+save "nom",start,size -> save binary
+save "nom",start,size,TAPE -> save tape file
+save "nom",start,size,AMSDOS -> save binary with Amsdos header
+save "nom",start,size,DSK,"dskname" -> save binary on DSK data format
+save "nom",start,size,DSK,"dskname",B -> select face
+save "nom",start,size,DSK,B -> current DSK, choose face
+save "nom",start,size,DSK -> current DSK, current face
+*/
+void __SAVE(struct s_assenv *ae) {
+ struct s_save cursave={0};
+ unsigned int offset=0,size=0;
+ int ko=1;
+
+ if (!ae->wl[ae->idx].t) {
+ /* nom de fichier entre quotes ou bien mot clef DSK */
+ if (!StringIsQuote(ae->wl[ae->idx+1].w)) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SAVE invalid filename quote\n");
+ ko=0;
+ } else {
+ if (!ae->wl[ae->idx+1].t) {
+ if (!ae->wl[ae->idx+2].t && ae->wl[ae->idx+3].t!=2) {
+ cursave.ibank=ae->activebank;
+ cursave.ioffset=ae->idx+2;
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+2].w,1); // si on utilise des variables ça évite la grouille post traitement...
+ cursave.isize=ae->idx+3;
+ ExpressionFastTranslate(ae,&ae->wl[ae->idx+3].w,1); // idem
+ cursave.iw=ae->idx+1;
+ cursave.irun=ae->current_run_idx;
+ if (!ae->wl[ae->idx+3].t) {
+ if (strcmp(ae->wl[ae->idx+4].w,"TAPE")==0) {
+ cursave.tape=1;
+ } else if (strcmp(ae->wl[ae->idx+4].w,"AMSDOS")==0) {
+ cursave.amsdos=1;
+ } else if (strcmp(ae->wl[ae->idx+4].w,"HOBETA")==0) {
+ cursave.hobeta=1;
+ } else if (strcmp(ae->wl[ae->idx+4].w,"DSK")==0) {
+#if TRACE_EDSK
+ printf("DSK SAVE order [bnk: %d ioff: %d isiz: %d iw=%d [%s] [%s]\n",cursave.ibank,cursave.ioffset,cursave.isize,cursave.iw,ae->wl[ae->idx+2].w,ae->wl[ae->idx+3].w);
+#endif
+ cursave.dsk=1;
+ if (!ae->wl[ae->idx+4].t) {
+ cursave.iwdskname=ae->idx+5;
+ if (!ae->wl[ae->idx+5].t) {
+ /* face selection - 0 as default */
+ switch (ae->wl[ae->idx+6].w[0]) {
+ case '1':
+ case 'B':
+ cursave.face=1;
+ break;
+ case '0':
+ case 'A':
+ default:
+ cursave.face=0;
+ break;
+ }
+ }
+ } else {
+ if (ae->nbsave && ae->save[ae->nbsave-1].iwdskname!=-1) {
+ cursave.iwdskname=ae->save[ae->nbsave-1].iwdskname; /* previous DSK */
+ cursave.face=ae->save[ae->nbsave-1].face; /* previous face */
+ } else {
+ cursave.iwdskname=-1;
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"cannot autoselect DSK as there was not a previous selection\n");
+ }
+ }
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SAVE 4th parameter must be empty or AMSDOS or DSK\n");
+ ko=0;
+ }
+ }
+ ObjectArrayAddDynamicValueConcat((void**)&ae->save,&ae->nbsave,&ae->maxsave,&cursave,sizeof(cursave));
+ ko=0;
+ }
+ }
+ }
+ }
+ if (ko) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use SAVE 'filename',offset,size[,AMSDOS|DSK[,A|B|'dskname'[,A|B]]]\n");
+ }
+ while (!ae->wl[ae->idx].t) ae->idx++;
+}
+
+
+void __MODULE(struct s_assenv *ae) {
+ if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) {
+ if (StringIsQuote(ae->wl[ae->idx+1].w)) {
+ if (ae->modulen || ae->module) {
+ MemFree(ae->module);
+ }
+ ae->modulen=strlen(ae->wl[ae->idx+1].w);
+ ae->module=MemMalloc(ae->modulen);
+ /* duplicate and remove quotes */
+ strcpy(ae->module,ae->wl[ae->idx+1].w+1);
+ ae->module[--ae->modulen]=0;
+ } else {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"MODULE directive need one text parameter\n");
+ }
+ ae->idx++;
+ } else {
+ if (ae->module) MemFree(ae->module);
+ ae->module=NULL;
+ }
+}
+
+void __TIMESTR(struct s_assenv *ae) {
+
+}
+
+struct s_asm_keyword instruction[]={
+{"LD",0,_LD},
+{"DEC",0,_DEC},
+{"INC",0,_INC},
+{"ADD",0,_ADD},
+{"SUB",0,_SUB},
+{"OR",0,_OR},
+{"AND",0,_AND},
+{"XOR",0,_XOR},
+{"POP",0,_POP},
+{"PUSH",0,_PUSH},
+{"DJNZ",0,_DJNZ},
+{"JR",0,_JR},
+{"JP",0,_JP},
+{"CALL",0,_CALL},
+{"RET",0,_RET},
+{"EX",0,_EX},
+{"ADC",0,_ADC},
+{"SBC",0,_SBC},
+{"EXA",0,_EXA},
+{"EXX",0,_EXX},
+{"CP",0,_CP},
+{"BIT",0,_BIT},
+{"RES",0,_RES},
+{"SET",0,_SET},
+{"IN",0,_IN},
+{"OUT",0,_OUT},
+{"RLC",0,_RLC},
+{"RRC",0,_RRC},
+{"RL",0,_RL},
+{"RR",0,_RR},
+{"SLA",0,_SLA},
+{"SRA",0,_SRA},
+{"SLL",0,_SLL},
+{"SL1",0,_SLL},
+{"SRL",0,_SRL},
+{"RST",0,_RST},
+{"HALT",0,_HALT},
+{"DI",0,_DI},
+{"EI",0,_EI},
+{"NOP",0,_NOP},
+{"DEFR",0,_DEFR},
+{"DEFB",0,_DEFB},
+{"DEFM",0,_DEFB},
+{"DR",0,_DEFR},
+{"DM",0,_DEFB},
+{"DB",0,_DEFB},
+{"DEFW",0,_DEFW},
+{"DW",0,_DEFW},
+{"DEFS",0,_DEFS},
+{"DS",0,_DEFS},
+{"STR",0,_STR},
+{"LDI",0,_LDI},
+{"LDIR",0,_LDIR},
+{"OUTI",0,_OUTI},
+{"INI",0,_INI},
+{"RLCA",0,_RLCA},
+{"RRCA",0,_RRCA},
+{"NEG",0,_NEG},
+{"RLA",0,_RLA},
+{"RRA",0,_RRA},
+{"RLD",0,_RLD},
+{"RRD",0,_RRD},
+{"DAA",0,_DAA},
+{"CPL",0,_CPL},
+{"SCF",0,_SCF},
+{"LDD",0,_LDD},
+{"LDDR",0,_LDDR},
+{"CCF",0,_CCF},
+{"OUTD",0,_OUTD},
+{"IND",0,_IND},
+{"RETI",0,_RETI},
+{"RETN",0,_RETN},
+{"IM",0,_IM},
+{"DEFI",0,_DEFI},
+{"CPD",0,_CPD},
+{"CPI",0,_CPI},
+{"CPDR",0,_CPDR},
+{"CPIR",0,_CPIR},
+{"OTDR",0,_OTDR},
+{"OTIR",0,_OTIR},
+{"INDR",0,_INDR},
+{"INIR",0,_INIR},
+{"REPEAT",0,__REPEAT},
+{"REND",0,__REND},
+{"ENDREPEAT",0,__REND},
+{"ENDREP",0,__REND},
+{"UNTIL",0,__UNTIL},
+{"ORG",0,__ORG},
+{"PROTECT",0,__PROTECT},
+{"WHILE",0,__WHILE},
+{"WEND",0,__WEND},
+{"HEXBIN",0,__HEXBIN},
+{"ALIGN",0,__ALIGN},
+{"ELSEIF",0,__ELSEIF},
+{"ELSE",0,__ELSE},
+{"IF",0,__IF},
+{"ENDIF",0,__ENDIF},
+{"IFNOT",0,__IFNOT},
+{"IFDEF",0,__IFDEF},
+{"IFNDEF",0,__IFNDEF},
+{"IFUSED",0,__IFUSED},
+{"IFNUSED",0,__IFNUSED},
+{"UNDEF",0,__UNDEF},
+{"CASE",0,__CASE},
+{"BREAK",0,__BREAK},
+{"DEFAULT",0,__DEFAULT},
+{"SWITCH",0,__SWITCH},
+{"ENDSWITCH",0,__ENDSWITCH},
+{"WRITE",0,__WRITE},
+{"CODE",0,__CODE},
+{"NOCODE",0,__NOCODE},
+{"MEMSPACE",0,__MEMSPACE},
+{"MACRO",0,__MACRO},
+{"TICKER",0,__TICKER},
+{"LET",0,__LET},
+{"ASSERT",0,__ASSERT},
+{"CHARSET",0,__CHARSET},
+{"RUN",0,__RUN},
+{"SAVE",0,__SAVE},
+{"BRK",0,__BRK},
+{"NOLIST",0,__NOLIST},
+{"LIST",0,__LIST},
+{"STOP",0,__STOP},
+{"PRINT",0,__PRINT},
+{"FAIL",0,__FAIL},
+{"BREAKPOINT",0,__BREAKPOINT},
+{"BANK",0,__BANK},
+{"BANKSET",0,__BANKSET},
+{"NAMEBANK",0,__NameBANK},
+{"LIMIT",0,__LIMIT},
+{"LZEXO",0,__LZEXO},
+{"LZX7",0,__LZX7},
+{"LZ4",0,__LZ4},
+{"LZ48",0,__LZ48},
+{"LZ49",0,__LZ49},
+{"LZCLOSE",0,__LZCLOSE},
+{"BUILDZX",0,__BUILDZX},
+{"BUILDCPR",0,__BUILDCPR},
+{"BUILDSNA",0,__BUILDSNA},
+{"BUILDROM",0,__BUILDROM},
+{"BUILDTAPE",0,__BUILDTAPE},
+{"SETCPC",0,__SETCPC},
+{"SETCRTC",0,__SETCRTC},
+{"AMSDOS",0,__AMSDOS},
+{"OTD",0,_OUTD},
+{"OTI",0,_OUTI},
+{"SHL",0,_SLA},
+{"SHR",0,_SRL},
+{"STRUCT",0,__STRUCT},
+{"ENDSTRUCT",0,__ENDSTRUCT},
+{"ENDS",0,__ENDSTRUCT},
+{"NOEXPORT",0,__NOEXPORT},
+{"ENOEXPORT",0,__ENOEXPORT},
+{"",0,NULL}
+};
+
+int Assemble(struct s_assenv *ae, unsigned char **dataout, int *lenout, struct s_rasm_info **debug)
+{
+ #undef FUNC
+ #define FUNC "Assemble"
+
+ unsigned char *AmsdosHeader;
+ struct s_expression curexp={0};
+ struct s_wordlist *wordlist;
+ struct s_expr_dico curdico={0};
+ struct s_label *curlabel;
+ int icrc,curcrc,i,j,k;
+ unsigned char *lzdata=NULL;
+ int lzlen,lzshift,input_size;
+ size_t slzlen;
+ unsigned char *input_data;
+ struct s_orgzone orgzone={0};
+ int iorgzone,ibank,offset,endoffset;
+ int il,maxrom;
+ char *TMP_filename=NULL;
+ int minmem=65536,maxmem=0,lzmove;
+ char symbol_line[1024];
+ int ifast,executed;
+ /* debug */
+ int curii,inhibe;
+ int ok;
+
+ rasm_printf(ae,KAYGREEN"Assembling\n");
+#if TRACE_ASSEMBLE
+printf("assembling\n");
+#endif
+#if TRACE_GENERALE
+printf("*** assembling ***\n");
+#endif
+
+ ae->retdebug=debug;
+
+ srand((unsigned)time(0));
+
+ wordlist=ae->wl;
+ ae->wl=wordlist;
+ /* start outside crunched section */
+ ae->lz=-1;
+
+ /* default orgzone */
+ orgzone.ibank=BANK_MAX_NUMBER;
+ ObjectArrayAddDynamicValueConcat((void**)&ae->orgzone,&ae->io,&ae->mo,&orgzone,sizeof(orgzone));
+ ___output=___internal_output;
+ /* init des automates */
+ InitAutomate(ae->AutomateHexa,AutomateHexaDefinition);
+ InitAutomate(ae->AutomateDigit,AutomateDigitDefinition);
+ InitAutomate(ae->AutomateValidLabel,AutomateValidLabelDefinition);
+ InitAutomate(ae->AutomateValidLabelFirst,AutomateValidLabelFirstDefinition);
+ InitAutomate(ae->AutomateExpressionValidCharExtended,AutomateExpressionValidCharExtendedDefinition);
+ InitAutomate(ae->AutomateExpressionValidCharFirst,AutomateExpressionValidCharFirstDefinition);
+ InitAutomate(ae->AutomateExpressionValidChar,AutomateExpressionValidCharDefinition);
+ ae->AutomateExpressionDecision['<']='<';
+ ae->AutomateExpressionDecision['>']='>';
+ ae->AutomateExpressionDecision['=']='=';
+ ae->AutomateExpressionDecision['!']='!';
+ ae->AutomateExpressionDecision[0]='E';
+ /* gestion d'alias */
+ ae->AutomateExpressionDecision['~']='~';
+ /* set operator precedence */
+ if (!ae->maxam) {
+ for (i=0;i<256;i++) {
+ switch (i) {
+ /* priority 0 */
+ case '(':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_OPEN;ae->AutomateElement[i].priority=0;break;
+ case ')':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_CLOSE;ae->AutomateElement[i].priority=0;break;
+ /* priority 1 */
+ case 'b':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_NOT;ae->AutomateElement[i].priority=1;break;
+ /* priority 2 */
+ case '*':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_MUL;ae->AutomateElement[i].priority=2;break;
+ case '/':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_DIV;ae->AutomateElement[i].priority=2;break;
+ case 'm':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_MOD;ae->AutomateElement[i].priority=2;break;
+ /* priority 3 */
+ case '+':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_ADD;ae->AutomateElement[i].priority=3;break;
+ case '-':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_SUB;ae->AutomateElement[i].priority=3;break;
+ /* priority 4 */
+ case '[':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_SHL;ae->AutomateElement[i].priority=4;break;
+ case ']':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_SHR;ae->AutomateElement[i].priority=4;break;
+ /* priority 5 */
+ case 'l':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_LOWER;ae->AutomateElement[i].priority=5;break;
+ case 'g':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_GREATER;ae->AutomateElement[i].priority=5;break;
+ case 'e':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_EQUAL;ae->AutomateElement[i].priority=5;break;
+ case 'n':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_NOTEQUAL;ae->AutomateElement[i].priority=5;break;
+ case 'k':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_LOWEREQ;ae->AutomateElement[i].priority=5;break;
+ case 'h':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_GREATEREQ;ae->AutomateElement[i].priority=5;break;
+ /* priority 6 */
+ case '&':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_AND;ae->AutomateElement[i].priority=6;break;
+ /* priority 7 */
+ case '^':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_XOR;ae->AutomateElement[i].priority=7;break;
+ /* priority 8 */
+ case '|':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_OR;ae->AutomateElement[i].priority=8;break;
+ /* priority 9 */
+ case 'a':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_BAND;ae->AutomateElement[i].priority=9;break;
+ /* priority 10 */
+ case 'o':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_BOR;ae->AutomateElement[i].priority=10;break;
+ default:ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_END;
+ }
+ }
+ } else {
+ for (i=0;i<256;i++) {
+ switch (i) {
+ /* priority 0 */
+ case '(':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_OPEN;ae->AutomateElement[i].priority=0;break;
+ case ')':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_CLOSE;ae->AutomateElement[i].priority=0;break;
+ /* priority 0.5 */
+ case 'b':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_NOT;ae->AutomateElement[i].priority=128;break;
+ /* priority 1 */
+ case '*':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_MUL;ae->AutomateElement[i].priority=464;break;
+ case '/':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_DIV;ae->AutomateElement[i].priority=464;break;
+ case 'm':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_MOD;ae->AutomateElement[i].priority=464;break;
+ case '+':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_ADD;ae->AutomateElement[i].priority=464;break;
+ case '-':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_SUB;ae->AutomateElement[i].priority=464;break;
+ case '[':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_SHL;ae->AutomateElement[i].priority=464;break;
+ case ']':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_SHR;ae->AutomateElement[i].priority=464;break;
+ case '&':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_AND;ae->AutomateElement[i].priority=464;break;
+ case '^':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_XOR;ae->AutomateElement[i].priority=464;break;
+ case '|':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_OR;ae->AutomateElement[i].priority=464;break;
+ /* priority 2 */
+ case 'l':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_LOWER;ae->AutomateElement[i].priority=664;break;
+ case 'g':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_GREATER;ae->AutomateElement[i].priority=664;break;
+ case 'e':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_EQUAL;ae->AutomateElement[i].priority=664;break;
+ case 'n':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_NOTEQUAL;ae->AutomateElement[i].priority=664;break;
+ case 'k':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_LOWEREQ;ae->AutomateElement[i].priority=664;break;
+ case 'h':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_GREATEREQ;ae->AutomateElement[i].priority=664;break;
+ /* priority 3 */
+ case 'a':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_BAND;ae->AutomateElement[i].priority=6128;break;
+ case 'o':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_BOR;ae->AutomateElement[i].priority=6128;break;
+ default:ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_END;
+ }
+ }
+ }
+
+ /* psg conversion */
+ for (i=j=0;i<100;i++) ae->psgtab[j++]=0;
+ for (i=0;i<49;i++) ae->psgtab[j++]=13;
+ for (i=0;i<35;i++) ae->psgtab[j++]=14;
+ for (i=0;i<72;i++) ae->psgtab[j++]=15;
+ if (j!=256) {
+ rasm_printf(ae,"Internal error with PSG conversion table\n");
+ exit(-44);
+ }
+ for (i=j=0;i<1;i++) ae->psgfine[j++]=0;
+ for (i=0;i<1;i++) ae->psgfine[j++]=1;
+ for (i=0;i<1;i++) ae->psgfine[j++]=2;
+ for (i=0;i<2;i++) ae->psgfine[j++]=3;
+ for (i=0;i<2;i++) ae->psgfine[j++]=4;
+ for (i=0;i<2;i++) ae->psgfine[j++]=5;
+ for (i=0;i<3;i++) ae->psgfine[j++]=6;
+ for (i=0;i<4;i++) ae->psgfine[j++]=7;
+ for (i=0;i<7;i++) ae->psgfine[j++]=8;
+ for (i=0;i<9;i++) ae->psgfine[j++]=9;
+ for (i=0;i<13;i++) ae->psgfine[j++]=10;
+ for (i=0;i<19;i++) ae->psgfine[j++]=11;
+ for (i=0;i<27;i++) ae->psgfine[j++]=12;
+ for (i=0;i<37;i++) ae->psgfine[j++]=13;
+ for (i=0;i<53;i++) ae->psgfine[j++]=14;
+ for (i=0;i<75;i++) ae->psgfine[j++]=15;
+ if (j!=256) {
+ rasm_printf(ae,"Internal error with PSG conversion table\n");
+ exit(-44);
+ }
+ /* default var */
+ ae->autorise_export=1;
+ ExpressionSetDicoVar(ae,"PI",3.1415926545);
+ ExpressionSetDicoVar(ae,"ASSEMBLER_RASM",1);
+
+ /* add a fictive expression to simplify test when parsing expressions */
+ ObjectArrayAddDynamicValueConcat((void **)&ae->expression,&ae->ie,&ae->me,&curexp,sizeof(curexp));
+
+ /* compute CRC for keywords and directives */
+ for (icrc=0;instruction[icrc].mnemo[0];icrc++) instruction[icrc].crc=GetCRC(instruction[icrc].mnemo);
+ for (icrc=0;math_keyword[icrc].mnemo[0];icrc++) math_keyword[icrc].crc=GetCRC(math_keyword[icrc].mnemo);
+
+ if (ae->as80==1) { /* not for UZ80 */
+ for (icrc=0;instruction[icrc].mnemo[0];icrc++) {
+ if (strcmp(instruction[icrc].mnemo,"DEFB")==0 || strcmp(instruction[icrc].mnemo,"DB")==0) {
+ instruction[icrc].makemnemo=_DEFB_as80;
+ } else if (strcmp(instruction[icrc].mnemo,"DEFW")==0 || strcmp(instruction[icrc].mnemo,"DW")==0) {
+ instruction[icrc].makemnemo=_DEFW_as80;
+ } else if (strcmp(instruction[icrc].mnemo,"DEFI")==0) {
+ instruction[icrc].makemnemo=_DEFI_as80;
+ }
+ }
+ }
+
+ for (icrc=0;instruction[icrc].mnemo[0];icrc++) {
+ /* get indexes for DEF instructions */
+ if (strcmp(instruction[icrc].mnemo,"DEFB")==0) {
+ ICRC_DEFB=icrc;
+ } else if (strcmp(instruction[icrc].mnemo,"DB")==0) {
+ ICRC_DB=icrc;
+ } else if (strcmp(instruction[icrc].mnemo,"DEFW")==0) {
+ ICRC_DEFW=icrc;
+ } else if (strcmp(instruction[icrc].mnemo,"DW")==0) {
+ ICRC_DW=icrc;
+ } else if (strcmp(instruction[icrc].mnemo,"DEFR")==0) {
+ ICRC_DEFR=icrc;
+ } else if (strcmp(instruction[icrc].mnemo,"DR")==0) {
+ ICRC_DR=icrc;
+ } else if (strcmp(instruction[icrc].mnemo,"DEFS")==0) {
+ ICRC_DEFS=icrc;
+ } else if (strcmp(instruction[icrc].mnemo,"DS")==0) {
+ ICRC_DS=icrc;
+ } else if (strcmp(instruction[icrc].mnemo,"DEFI")==0) {
+ ICRC_DEFI=icrc;
+ }
+ }
+
+ /* Execution des mots clefs */
+ /**********************************************************
+ A S S E M B L I N G M A I N L O O P
+ **********************************************************/
+#if TRACE_ASSEMBLE
+printf("init ok\n");
+#endif
+#if TRACE_GENERALE
+printf("-loop\n");
+#endif
+
+ ae->idx=1;
+ while (wordlist[ae->idx].t!=2) {
+ curcrc=GetCRC(wordlist[ae->idx].w);
+ /*********************
+ d e b u g i n f o
+ *********************/
+ #if TRACE_ASSEMBLE
+ {
+ int iiii=0;
+ printf(KVERBOSE"%d [%s] L%d [%s]e=%d ",ae->idx,ae->filename[wordlist[ae->idx].ifile],wordlist[ae->idx].l,wordlist[ae->idx].w,wordlist[ae->idx].e);
+ while (!wordlist[ae->idx+iiii++].t) rasm_printf(ae," [%s]e=%d ",wordlist[ae->idx+iiii].w,wordlist[ae->idx+iiii].e);
+
+ for (iiii=0;iiii<ae->imacropos;iiii++) {
+ printf("M[%d] s=%d e=%d ",iiii,ae->macropos[iiii].start,ae->macropos[iiii].end);
+ }
+ printf("\n");
+ }
+ #endif
+
+ /********************************************************************
+ c o n d i t i o n n a l a s s e m b l y m a n a g e m e n t
+ ********************************************************************/
+ if (ae->ii || ae->isw) {
+ /* inhibition of if/endif */
+ for (inhibe=curii=0;curii<ae->ii;curii++) {
+ if (!ae->ifthen[curii].v || ae->ifthen[curii].v==-1) {
+ inhibe=1;
+ break;
+ }
+ }
+ /* when inhibited we are looking only for a IF/IFDEF/IFNOT/IFNDEF/ELSE/ELSEIF/ENDIF or SWITCH/CASE/DEFAULT/ENDSWITCH */
+ if (inhibe) {
+ /* this section does NOT need to be agressively optimized !!! */
+ if (curcrc==CRC_ELSEIF && strcmp(wordlist[ae->idx].w,"ELSEIF")==0) {
+ /* true IF needs to be done ONLY on the active level */
+ if (curii==ae->ii-1) __ELSEIF(ae); else __ELSEIF_light(ae);
+ } else if (curcrc==CRC_ELSE && strcmp(wordlist[ae->idx].w,"ELSE")==0) {
+ __ELSE(ae);
+ } else if (curcrc==CRC_ENDIF && strcmp(wordlist[ae->idx].w,"ENDIF")==0) {
+ __ENDIF(ae);
+ } else if (curcrc==CRC_IF && strcmp(wordlist[ae->idx].w,"IF")==0) {
+ /* as we are inhibited we do not have to truly compute IF */
+ __IF_light(ae);
+ } else if (curcrc==CRC_IFDEF && strcmp(wordlist[ae->idx].w,"IFDEF")==0) {
+ __IFDEF_light(ae);
+ } else if (curcrc==CRC_IFNOT && strcmp(wordlist[ae->idx].w,"IFNOT")==0) {
+ __IFNOT_light(ae);
+ } else if (curcrc==CRC_IFUSED && strcmp(wordlist[ae->idx].w,"IFUSED")==0) {
+ __IFUSED_light(ae);
+ } else if (curcrc==CRC_IFNUSED && strcmp(wordlist[ae->idx].w,"IFNUSED")==0) {
+ __IFNUSED_light(ae);
+ } else if (curcrc==CRC_IFNDEF && strcmp(wordlist[ae->idx].w,"IFNDEF")==0) {
+ __IFNDEF_light(ae);
+ } else if (curcrc==CRC_SWITCH && strcmp(wordlist[ae->idx].w,"SWITCH")==0) {
+ __SWITCH_light(ae);
+ } else if (curcrc==CRC_CASE && strcmp(wordlist[ae->idx].w,"CASE")==0) {
+ __CASE_light(ae);
+ } else if (curcrc==CRC_ENDSWITCH && strcmp(wordlist[ae->idx].w,"ENDSWITCH")==0) {
+ __ENDSWITCH(ae);
+ } else if (curcrc==CRC_BREAK && strcmp(wordlist[ae->idx].w,"BREAK")==0) {
+ __BREAK_light(ae);
+ } else if (curcrc==CRC_DEFAULT && strcmp(wordlist[ae->idx].w,"DEFAULT")==0) {
+ __DEFAULT_light(ae);
+ }
+ while (wordlist[ae->idx].t==0) ae->idx++;
+ ae->idx++;
+ continue;
+ } else {
+ /* inhibition of switch/case */
+ for (curii=0;curii<ae->isw;curii++) {
+ if (!ae->switchcase[curii].execute) {
+ inhibe=2;
+ break;
+ }
+ }
+ if (inhibe) {
+ /* this section does NOT need to be agressively optimized !!! */
+ if (curcrc==CRC_CASE && strcmp(wordlist[ae->idx].w,"CASE")==0) {
+ __CASE(ae);
+ } else if (curcrc==CRC_ENDSWITCH && strcmp(wordlist[ae->idx].w,"ENDSWITCH")==0) {
+ __ENDSWITCH(ae);
+ } else if (curcrc==CRC_IF && strcmp(wordlist[ae->idx].w,"IF")==0) {
+ /* as we are inhibited we do not have to truly compute IF */
+ __IF_light(ae);
+ } else if (curcrc==CRC_IFDEF && strcmp(wordlist[ae->idx].w,"IFDEF")==0) {
+ __IFDEF(ae);
+ } else if (curcrc==CRC_IFNOT && strcmp(wordlist[ae->idx].w,"IFNOT")==0) {
+ __IFNOT(ae);
+ } else if (curcrc==CRC_ELSE && strcmp(wordlist[ae->idx].w,"ELSE")==0) {
+ __ELSE(ae);
+ } else if (curcrc==CRC_ENDIF && strcmp(wordlist[ae->idx].w,"ENDIF")==0) {
+ __ENDIF(ae);
+ } else if (curcrc==CRC_ELSEIF && strcmp(wordlist[ae->idx].w,"ELSEIF")==0) {
+ __ELSEIF(ae);
+ } else if (curcrc==CRC_IFUSED && strcmp(wordlist[ae->idx].w,"IFUSED")==0) {
+ __IFUSED(ae);
+ } else if (curcrc==CRC_IFNUSED && strcmp(wordlist[ae->idx].w,"IFNUSED")==0) {
+ __IFNUSED(ae);
+ } else if (curcrc==CRC_IFNDEF && strcmp(wordlist[ae->idx].w,"IFNDEF")==0) {
+ __IFNDEF(ae);
+ } else if (curcrc==CRC_SWITCH && strcmp(wordlist[ae->idx].w,"SWITCH")==0) {
+ __SWITCH(ae);
+ } else if (curcrc==CRC_BREAK && strcmp(wordlist[ae->idx].w,"BREAK")==0) {
+ __BREAK(ae);
+ } else if (curcrc==CRC_DEFAULT && strcmp(wordlist[ae->idx].w,"DEFAULT")==0) {
+ __DEFAULT(ae);
+ }
+ while (wordlist[ae->idx].t==0) ae->idx++;
+ ae->idx++;
+ continue;
+ }
+ }
+ }
+ if (ae->imacropos) {
+ /* are we still in a macro? */
+ if (ae->idx>=ae->macropos[0].end) {
+ /* are we out of all repetition blocks? */
+ if (!ae->ir && !ae->iw) {
+ ae->imacropos=0;
+
+ /* quand on sort du local, on récupère le dernier label global */
+ if (ae->lastsuperglobal!=ae->lastgloballabel && ae->lastsuperglobal) {
+ if (ae->lastglobalalloc) {
+ MemFree(ae->lastgloballabel);
+ ae->lastglobalalloc=0;
+ }
+ ae->lastgloballabel=ae->lastsuperglobal;
+ ae->lastgloballabellen=strlen(ae->lastgloballabel);
+ }
+ }
+ }
+ }
+ /*****************************************
+ e x e c u t e i n s t r u c t i o n
+ *****************************************/
+ executed=0;
+ if ((ifast=ae->fastmatch[(int)wordlist[ae->idx].w[0]])!=-1) {
+ while (instruction[ifast].mnemo[0]==wordlist[ae->idx].w[0]) {
+ if (instruction[ifast].crc==curcrc && strcmp(instruction[ifast].mnemo,wordlist[ae->idx].w)==0) {
+#if TRACE_ASSEMBLE
+printf("-> mnemo\n");
+#endif
+ instruction[ifast].makemnemo(ae);
+ executed=1;
+ break;
+ }
+ ifast++;
+ }
+ }
+ /*****************************************
+ e x e c u t e m a c r o
+ *****************************************/
+ if (!executed) {
+ /* is it a macro? */
+ if ((ifast=SearchMacro(ae,curcrc,wordlist[ae->idx].w))>=0) {
+#if TRACE_ASSEMBLE
+printf("-> macro\n");
+#endif
+ wordlist=__MACRO_EXECUTE(ae,ifast);
+ continue;
+ }
+ }
+ /*********************************************************************
+ e x e c u t e e x p r e s s i o n o r p u s h l a b e l
+ *********************************************************************/
+ if (!ae->stop) {
+ if (!executed) {
+ /* no instruction executed, this is a label or an assignement */
+ if (wordlist[ae->idx].e) {
+#if TRACE_ASSEMBLE
+printf("-> expr\n");
+#endif
+ ExpressionFastTranslate(ae,&wordlist[ae->idx].w,0);
+ ComputeExpression(ae,wordlist[ae->idx].w,ae->codeadr,0,0);
+ } else {
+#if TRACE_ASSEMBLE
+printf("-> label\n");
+#endif
+ PushLabel(ae);
+ }
+ } else {
+#if TRACE_ASSEMBLE
+printf("-> ajuste IDX\n");
+#endif
+ while (!wordlist[ae->idx].t) {
+ ae->idx++;
+ }
+ }
+ ae->idx++;
+ } else {
+#if TRACE_ASSEMBLE
+printf("-> STOP\n");
+#endif
+ break;
+ }
+ }
+#if TRACE_ASSEMBLE
+ rasm_printf(ae,KVERBOSE"%d [%s] L%d [%s] fin de la liste de mots\n",ae->idx,ae->filename[wordlist[ae->idx].ifile],wordlist[ae->idx].l,wordlist[ae->idx].w);
+ printf("check ORG\n");
+#endif
+#if TRACE_GENERALE
+printf("-check ORG\n");
+#endif
+
+ if (!ae->stop) {
+ /* end of assembly, check there is no opened struct */
+ if (ae->getstruct) {
+ MakeError(ae,ae->backup_filename,ae->backup_line,"STRUCT declaration was not closed\n");
+ }
+ /* end of assembly, close the last ORG zone */
+ if (ae->io) {
+ ae->orgzone[ae->io-1].memend=ae->outputadr;
+ }
+ OverWriteCheck(ae);
+ /* end of assembly, close crunched zone (if any) */
+ __internal_UpdateLZBlockIfAny(ae);
+
+ /* end of assembly, check for opened repeat and opened while loop */
+ for (i=0;i<ae->ir;i++) {
+ MakeError(ae,ae->filename[wordlist[ae->repeat[i].start].ifile],wordlist[ae->repeat[i].start].l,"REPEAT was not closed\n");
+ }
+ for (i=0;i<ae->iw;i++) {
+ MakeError(ae,ae->filename[wordlist[ae->whilewend[i].start].ifile],wordlist[ae->whilewend[i].start].l,"WHILE was not closed\n");
+ }
+ /* is there any IF opened? -> need an evolution for a better error message */
+ for (i=0;i<ae->ii;i++) {
+ char instr[32];
+ switch (ae->ifthen[i].type) {
+ case E_IFTHEN_TYPE_IF:strcpy(instr,"IF");break;
+ case E_IFTHEN_TYPE_IFNOT:strcpy(instr,"IFNOT");break;
+ case E_IFTHEN_TYPE_IFDEF:strcpy(instr,"IFDEF");break;
+ case E_IFTHEN_TYPE_IFNDEF:strcpy(instr,"IFNDEF");break;
+ case E_IFTHEN_TYPE_ELSE:strcpy(instr,"ELSE");break;
+ case E_IFTHEN_TYPE_ELSEIF:strcpy(instr,"ELSEIF");break;
+ case E_IFTHEN_TYPE_IFUSED:strcpy(instr,"IFUSED");break;
+ case E_IFTHEN_TYPE_IFNUSED:strcpy(instr,"IFNUSED");break;
+ default:strcpy(instr,"<unknown>");
+ }
+ MakeError(ae,ae->ifthen[i].filename,ae->ifthen[i].line,"%s conditionnal block was not closed\n",instr);
+ }
+ }
+#if TRACE_ASSEMBLE
+printf("crunch if any\n");
+#endif
+ /***************************************************
+ c r u n c h L Z s e c t i o n s
+ ***************************************************/
+ if (!ae->stop || !ae->nberr) {
+ for (i=0;i<ae->ilz;i++) {
+ /* compute labels and expression inside crunched blocks */
+ PopAllExpression(ae,i);
+
+ ae->curlz=i;
+ iorgzone=ae->lzsection[i].iorgzone;
+ ibank=ae->lzsection[i].ibank;
+ input_data=&ae->mem[ae->lzsection[i].ibank][ae->lzsection[i].memstart];
+ input_size=ae->lzsection[i].memend-ae->lzsection[i].memstart;
+//printf("grouik (%d) %s\n",ae->lzsection[i].lzversion,ae->lzsection[i].lzversion==8?"mizou":"");
+ if (!input_size) {
+ rasm_printf(ae,KWARNING"[%s:%d] Warning: crunched section is empty\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ } else {
+ switch (ae->lzsection[i].lzversion) {
+ case 7:
+ #ifndef NO_3RD_PARTIES
+ lzdata=ZX7_compress(optimize(input_data, input_size), input_data, input_size, &slzlen);
+ lzlen=slzlen;
+ #endif
+ break;
+ case 4:
+ #ifndef NO_3RD_PARTIES
+ lzdata=LZ4_crunch(input_data,input_size,&lzlen);
+ #endif
+ break;
+ case 8:
+ #ifndef NO_3RD_PARTIES
+ rasm_printf(ae,KWARNING"Exomizer is crunching %.1fkb this may take a while, be patient...\n",input_size/1024.0);
+
+ lzdata=Exomizer_crunch(input_data,input_size,&lzlen);
+ #endif
+ break;
+ case 48:
+ lzdata=LZ48_crunch(input_data,input_size,&lzlen);
+ break;
+ case 49:
+ lzdata=LZ49_crunch(input_data,input_size,&lzlen);
+ break;
+ default:
+ rasm_printf(ae,"Internal error - unknown crunch method %d\n",ae->lzsection[i].lzversion);
+ exit(-12);
+ }
+ }
+ //rasm_printf(ae,"lzsection[%d] type=%d start=%04X end=%04X crunched size=%d\n",i,ae->lzsection[i].lzversion,ae->lzsection[i].memstart,ae->lzsection[i].memend,lzlen);
+
+ if (input_size<lzlen) {
+ MakeError(ae,ae->filename[ae->wl[ae->lzsection[i].iw].ifile],ae->wl[ae->lzsection[i].iw].l,"As the LZ section cannot crunch data, Rasm may not guarantee assembled file!\n");
+ }
+
+ lzshift=lzlen-(ae->lzsection[i].memend-ae->lzsection[i].memstart);
+ if (lzshift>0) {
+ MemMove(ae->mem[ae->lzsection[i].ibank]+ae->lzsection[i].memend+lzshift,ae->mem[ae->lzsection[i].ibank]+ae->lzsection[i].memend,65536-ae->lzsection[i].memend-lzshift);
+ } else if (lzshift<0) {
+ lzmove=ae->orgzone[iorgzone].memend-ae->lzsection[i].memend;
+ if (lzmove) {
+ MemMove(ae->mem[ae->lzsection[i].ibank]+ae->lzsection[i].memend+lzshift,ae->mem[ae->lzsection[i].ibank]+ae->lzsection[i].memend,lzmove);
+ }
+ }
+ memcpy(ae->mem[ae->lzsection[i].ibank]+ae->lzsection[i].memstart,lzdata,lzlen);
+ MemFree(lzdata);
+ /*******************************************************************
+ l a b e l a n d e x p r e s s i o n r e l o c a t i o n
+ *******************************************************************/
+ /* relocate labels in the same ORG zone AND after the current crunched section */
+ il=ae->lzsection[i].ilabel;
+ while (il<ae->il && ae->label[il].iorgzone==iorgzone && ae->label[il].ibank==ibank) {
+ curlabel=SearchLabel(ae,ae->label[il].iw!=-1?wordlist[ae->label[il].iw].w:ae->label[il].name,ae->label[il].crc);
+ /* CANNOT be NULL */
+ curlabel->ptr+=lzshift;
+ //printf("label [%s] shifte de %d valeur #%04X -> #%04X\n",curlabel->iw!=-1?wordlist[curlabel->iw].w:curlabel->name,lzshift,curlabel->ptr-lzshift,curlabel->ptr);
+ il++;
+ }
+ /* relocate expressions in the same ORG zone AND after the current crunched section */
+ il=ae->lzsection[i].iexpr;
+ while (il<ae->ie && ae->expression[il].iorgzone==iorgzone && ae->expression[il].ibank==ibank) {
+ ae->expression[il].wptr+=lzshift;
+ ae->expression[il].ptr+=lzshift;
+ //printf("expression [%s] shiftee ptr=#%04X wptr=#%04X\n", ae->expression[il].reference?ae->expression[il].reference:wordlist[ae->expression[il].iw].w, ae->expression[il].ptr, ae->expression[il].wptr);
+ il++;
+ }
+ /* relocate crunched sections in the same ORG zone AND after the current crunched section */
+ il=i+1;
+ while (il<ae->ilz && ae->lzsection[il].iorgzone==iorgzone && ae->lzsection[il].ibank==ibank) {
+ //rasm_printf(ae,"reloger lzsection[%d] O%d B%d\n",il,ae->lzsection[il].iorgzone,ae->lzsection[il].ibank);
+ ae->lzsection[il].memstart+=lzshift;
+ ae->lzsection[il].memend+=lzshift;
+ il++;
+ }
+ /* relocate current ORG zone */
+ ae->orgzone[iorgzone].memend+=lzshift;
+ }
+ if (ae->ilz) {
+ /* compute expression placed after the last crunched block */
+ PopAllExpression(ae,ae->ilz);
+ }
+ /* compute expression outside crunched blocks */
+ PopAllExpression(ae,-1);
+ }
+
+/***************************************************************************************************************************************************************************************
+****************************************************************************************************************************************************************************************
+ W R I T E O U T P U T F I L E S
+****************************************************************************************************************************************************************************************
+***************************************************************************************************************************************************************************************/
+ TMP_filename=MemMalloc(PATH_MAX);
+#if 0
+for (i=0;i<ae->io;i++) {
+printf("ORG[%02d] start=%04X end=%04X ibank=%d nocode=%d protect=%d\n",i,ae->orgzone[i].memstart,ae->orgzone[i].memend,ae->orgzone[i].ibank,ae->orgzone[i].nocode,ae->orgzone[i].protect);
+}
+#endif
+#if TRACE_ASSEMBLE
+printf("output files\n");
+#endif
+
+ if (!ae->nberr && !ae->checkmode) {
+
+ /* enregistrement des fichiers programmes par la commande SAVE */
+ PopAllSave(ae);
+
+ if (ae->nbsave==0 || ae->forcecpr || ae->forcesnapshot) {
+ /*********************************************
+ **********************************************
+ C A R T R I D G E
+ **********************************************
+ *********************************************/
+ if (ae->forcecpr) {
+ char ChunkName[32];
+ int ChunkSize;
+ int do_it=1;
+ unsigned char chunk_endian;
+
+ if (ae->cartridge_name) {
+ sprintf(TMP_filename,"%s",ae->cartridge_name);
+ } else {
+ sprintf(TMP_filename,"%s.cpr",ae->outputfilename);
+ }
+ FileRemoveIfExists(TMP_filename);
+
+ rasm_printf(ae,KIO"Write cartridge file %s\n",TMP_filename);
+ for (i=maxrom=0;i<ae->io;i++) {
+ if (ae->orgzone[i].ibank<32 && ae->orgzone[i].ibank>maxrom) maxrom=ae->orgzone[i].ibank;
+ }
+ /* construction du CPR */
+ /* header blablabla */
+ strcpy(ChunkName,"RIFF");
+ FileWriteBinary(TMP_filename,ChunkName,4);
+ ChunkSize=(maxrom+1)*(16384+8)+4;
+ chunk_endian=ChunkSize&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1);
+ chunk_endian=(ChunkSize>>8)&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1);
+ chunk_endian=(ChunkSize>>16)&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1);
+ chunk_endian=(ChunkSize>>24)&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1);
+ sprintf(ChunkName,"AMS!");
+ FileWriteBinary(TMP_filename,ChunkName,4);
+
+// for (j=0;j<ae->io;j++) {
+//printf("ORG[%03d]=B%02d/#%04X/#%04X\n",j,ae->orgzone[j].ibank,ae->orgzone[j].memstart,ae->orgzone[j].memend);
+// }
+ for (i=0;i<=maxrom;i++) {
+ offset=65536;
+ endoffset=0;
+ for (j=0;j<ae->io;j++) {
+ if (ae->orgzone[j].protect) continue; /* protected zones exclusion */
+ /* bank data may start anywhere (typically #0000 or #C000) */
+ if (ae->orgzone[j].ibank==i && ae->orgzone[j].memstart!=ae->orgzone[j].memend) {
+ if (ae->orgzone[j].memstart<offset) offset=ae->orgzone[j].memstart;
+ if (ae->orgzone[j].memend>endoffset) endoffset=ae->orgzone[j].memend;
+ }
+ }
+ if (endoffset>offset) {
+ int lm=0;
+ if (ae->iwnamebank[i]>0) {
+ lm=strlen(ae->wl[ae->iwnamebank[i]].w)-2;
+ }
+ rasm_printf(ae,KVERBOSE"WriteCPR bank %2d of %5d byte%s start at #%04X",i,endoffset-offset,endoffset-offset>1?"s":" ",offset);
+ if (endoffset-offset>16384) {
+ rasm_printf(ae,"\nROM is too big!!!\n");
+ FileWriteBinaryClose(TMP_filename);
+ FileRemoveIfExists(TMP_filename);
+ FreeAssenv(ae);
+ exit(ABORT_ERROR);
+ }
+ if (lm) {
+ rasm_printf(ae," (%-*.*s)\n",lm,lm,ae->wl[ae->iwnamebank[i]].w+1);
+ } else {
+ rasm_printf(ae,"\n");
+ }
+ } else {
+ rasm_printf(ae,KVERBOSE"WriteCPR bank %2d (empty)\n",i);
+ }
+ ChunkSize=16384;
+ sprintf(ChunkName,"cb%02d",i);
+ FileWriteBinary(TMP_filename,ChunkName,4);
+ chunk_endian=ChunkSize&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1);
+ chunk_endian=(ChunkSize>>8)&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1);
+ chunk_endian=(ChunkSize>>16)&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1);
+ chunk_endian=(ChunkSize>>24)&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1);
+ if (offset>0xC000) {
+ unsigned char filler[16384]={0};
+ ChunkSize=65536-offset;
+ if (ChunkSize) FileWriteBinary(TMP_filename,(char*)ae->mem[i]+offset,ChunkSize);
+ /* ADD zeros until the end of the bank */
+ FileWriteBinary(TMP_filename,(char*)filler,16384-ChunkSize);
+ } else {
+ FileWriteBinary(TMP_filename,(char*)ae->mem[i]+offset,ChunkSize);
+ }
+ }
+ FileWriteBinaryClose(TMP_filename);
+ rasm_printf(ae,"Total %d bank%s (%dK)\n",maxrom+1,maxrom+1>1?"s":"",(maxrom+1)*16);
+ /*********************************************
+ **********************************************
+ S N A P S H O T
+ **********************************************
+ *********************************************/
+ } else if (ae->forcesnapshot) {
+
+ if (ae->forcezx) {
+ unsigned char zxsnapheader[0x1A]={0};
+
+ if (ae->snapshot_name) {
+ sprintf(TMP_filename,"%s",ae->snapshot_name);
+ } else {
+ sprintf(TMP_filename,"%s.sna",ae->outputfilename);
+ }
+ FileRemoveIfExists(TMP_filename);
+
+ /* do we have a bankset? */
+ /* zx bootstrap */
+ zxsnapheader[0x13]=0; /* 0:DI 4:EI */
+ zxsnapheader[0x17]=ae->zxsnapshot.stack&0xFF;
+ zxsnapheader[0x18]=(ae->zxsnapshot.stack>>8)&0xFF;
+ zxsnapheader[0x19]=1; /* IM 1 */
+
+ //ae->zxsnapshot.stack&=0xFFFF;
+ ae->mem[0][ae->zxsnapshot.stack]=ae->zxsnapshot.run&0xFF;
+ ae->mem[0][ae->zxsnapshot.stack+1]=(ae->zxsnapshot.run>>8)&0xFF;
+
+ rasm_printf(ae,KIO"Write 48K ZX snapshot file %s\n",TMP_filename);
+
+ /* header */
+ FileWriteBinary(TMP_filename,(char *)&zxsnapheader,27);
+ /* data */
+ if (ae->bankset[0]) {
+ FileWriteBinary(TMP_filename,(char *)ae->mem[0]+16384,16384*3);
+ } else {
+ FileWriteBinary(TMP_filename,(char *)ae->mem[5],16384);
+ FileWriteBinary(TMP_filename,(char *)ae->mem[2],16384);
+ FileWriteBinary(TMP_filename,(char *)ae->mem[0],16384);
+ }
+ FileWriteBinaryClose(TMP_filename);
+ } else {
+ unsigned char packed[65536]={0};
+ unsigned char *rlebank=NULL;
+ char ChunkName[16];
+ int ChunkSize;
+ int do_it=1;
+ int bankset;
+ int noflood=0;
+
+ if (ae->snapshot.version==2 && ae->snapshot.CPCType>2) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: V2 snapshot cannot select a Plus model (forced to 6128)\n",GetCurrentFile(ae),ae->wl[ae->idx].l);
+ ae->snapshot.CPCType=2; /* 6128 */
+ }
+
+ if (ae->snapshot_name) {
+ sprintf(TMP_filename,"%s",ae->snapshot_name);
+ } else {
+ sprintf(TMP_filename,"%s.sna",ae->outputfilename);
+ }
+ FileRemoveIfExists(TMP_filename);
+
+ maxrom=-1;
+ for (i=0;i<ae->io;i++) {
+ if (ae->orgzone[i].ibank<BANK_MAX_NUMBER && ae->orgzone[i].ibank>maxrom && ae->orgzone[i].memstart!=ae->orgzone[i].memend) {
+ maxrom=ae->orgzone[i].ibank;
+ }
+ }
+
+ //printf("maxrom=%d\n",maxrom);
+ /* construction du SNA */
+ if (ae->snapshot.version==2) {
+ if (maxrom>=4) {
+ ae->snapshot.dumpsize[0]=128;
+ } else if (maxrom>=0) {
+ ae->snapshot.dumpsize[0]=64;
+ }
+ }
+ if (maxrom==-1) {
+ rasm_printf(ae,KWARNING"Warning: No byte were written in snapshot memory\n");
+ } else {
+ rasm_printf(ae,KIO"Write snapshot v%d file %s\n",ae->snapshot.version,TMP_filename);
+
+ /* header */
+ FileWriteBinary(TMP_filename,(char *)&ae->snapshot,0x100);
+ /* write all memory crunched */
+ for (i=0;i<=maxrom;i+=4) {
+ bankset=i>>2;
+ if (ae->bankset[bankset]) {
+ memcpy(packed,ae->mem[i],65536);
+ if (i<4 || i+4>maxrom) rasm_printf(ae,KVERBOSE"WriteSNA bank %2d,%d,%d,%d packed\n",i,i+1,i+2,i+3);
+ else if (!noflood) {rasm_printf(ae,KVERBOSE"[...]\n");noflood=1;}
+ } else {
+ memset(packed,0,65536);
+ for (k=0;k<4;k++) {
+ offset=65536;
+ endoffset=0;
+ for (j=0;j<ae->io;j++) {
+ if (ae->orgzone[j].protect) continue; /* protected zones exclusion */
+ /* bank data may start anywhere (typically #0000 or #C000) */
+ if (ae->orgzone[j].ibank==i+k && ae->orgzone[j].memstart!=ae->orgzone[j].memend) {
+ if (ae->orgzone[j].memstart<offset) offset=ae->orgzone[j].memstart;
+ if (ae->orgzone[j].memend>endoffset) endoffset=ae->orgzone[j].memend;
+ }
+ }
+ if (endoffset-offset>16384) {
+ rasm_printf(ae,KERROR"\nBANK is too big!!!\n");
+ FileWriteBinaryClose(TMP_filename);
+ FileRemoveIfExists(TMP_filename);
+ FreeAssenv(ae);
+ exit(ABORT_ERROR);
+ }
+ /* banks are gathered in the 64K block */
+ if (offset>0xC000) {
+ ChunkSize=65536-offset;
+ memcpy(packed+k*16384,(char*)ae->mem[i+k]+offset,ChunkSize);
+ } else {
+ memcpy(packed+k*16384,(char*)ae->mem[i+k]+offset,16384);
+ }
+
+ if (endoffset>offset) {
+ int lm=0;
+ if (ae->iwnamebank[i]>0) {
+ lm=strlen(ae->wl[ae->iwnamebank[i]].w)-2;
+ }
+ if (i<4 || i+4>maxrom) rasm_printf(ae,KVERBOSE"WriteSNA bank %2d of %5d byte%s start at #%04X",i+k,endoffset-offset,endoffset-offset>1?"s":" ",offset);
+ else if (!noflood) {rasm_printf(ae,KVERBOSE"[...]\n");noflood=1;}
+ if (endoffset-offset>16384) {
+ rasm_printf(ae,KERROR"\nRAM block is too big!!!\n");
+ FileWriteBinaryClose(TMP_filename);
+ FileRemoveIfExists(TMP_filename);
+ FreeAssenv(ae);
+ exit(ABORT_ERROR);
+ }
+ if (lm) {
+ if (i<4 || i+4>maxrom) rasm_printf(ae,KVERBOSE" (%-*.*s)\n",lm,lm,ae->wl[ae->iwnamebank[i+k]].w+1);
+ } else {
+ if (i<4 || i+4>maxrom) rasm_printf(ae,"\n");
+ }
+ } else {
+ if (i<4 || i+4>maxrom) rasm_printf(ae,KVERBOSE"WriteSNA bank %2d (empty)\n",i+k);
+ else if (!noflood) {rasm_printf(ae,KVERBOSE"[...]\n");noflood=1;}
+ }
+ }
+ }
+
+ if (ae->snapshot.version==2) {
+ /* snapshot v2 */
+ FileWriteBinary(TMP_filename,(char*)&packed,65536);
+ if (bankset) {
+ /* v2 snapshot is 128K maximum */
+ maxrom=7;
+ break;
+ }
+ } else {
+ /* compression par défaut avec snapshot v3 */
+ rlebank=EncodeSnapshotRLE(packed,&ChunkSize);
+
+ if (bankset>=0 && bankset<=8) {
+ sprintf(ChunkName,"MEM%d",bankset);
+ } else if (bankset>8 && bankset<=0x40) {
+ /* extended chunk for 4M extension -> MX09 to MX40 (hexa numbered) */
+ sprintf(ChunkName,"MX%02X",bankset);
+ } else {
+ MakeError(ae,"(core)",0,"internal error during snapshot write, please report (%d)\n",bankset);
+ }
+
+ FileWriteBinary(TMP_filename,ChunkName,4);
+ if (rlebank!=NULL) {
+ FileWriteBinary(TMP_filename,(char*)&ChunkSize,4);
+ FileWriteBinary(TMP_filename,(char*)rlebank,ChunkSize);
+ MemFree(rlebank);
+ } else {
+ ChunkSize=65536;
+ FileWriteBinary(TMP_filename,(char*)&packed,ChunkSize);
+ }
+ }
+ }
+
+ /**************************************************************
+ snapshot additional chunks in v3+ only
+ **************************************************************/
+ if (ae->snapshot.version>=3) {
+ /* export breakpoint */
+ if (ae->export_snabrk) {
+ /* BRKS chunk for Winape emulator (unofficial)
+
+ 2 bytes - adress
+ 1 byte - 0=base 64K / 1=extended
+ 2 bytes - condition (zeroed)
+ */
+ struct s_breakpoint breakpoint={0};
+ unsigned char *brkschunk=NULL;
+ unsigned int idx=8;
+
+ /* add labels and local labels to breakpoint pool (if any) */
+ for (i=0;i<ae->il;i++) {
+ if (!ae->label[i].name) {
+ if (strncmp(ae->wl[ae->label[i].iw].w,"BRK",3)==0) {
+ breakpoint.address=ae->label[i].ptr;
+ if (ae->label[i].ibank>3) breakpoint.bank=1; else breakpoint.bank=0;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->breakpoint,&ae->ibreakpoint,&ae->maxbreakpoint,&breakpoint,sizeof(struct s_breakpoint));
+ }
+ } else {
+ if (strncmp(ae->label[i].name,"@BRK",4)==0 || strstr(ae->label[i].name,".BRK")) {
+ breakpoint.address=ae->label[i].ptr;
+ if (ae->label[i].ibank>3) breakpoint.bank=1; else breakpoint.bank=0;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->breakpoint,&ae->ibreakpoint,&ae->maxbreakpoint,&breakpoint,sizeof(struct s_breakpoint));
+ }
+ }
+ }
+
+ brkschunk=MemMalloc(ae->ibreakpoint*5+8);
+ strcpy((char *)brkschunk,"BRKS");
+
+ for (i=0;i<ae->ibreakpoint;i++) {
+ brkschunk[idx++]=ae->breakpoint[i].address&0xFF;
+ brkschunk[idx++]=(ae->breakpoint[i].address&0xFF00)/256;
+ brkschunk[idx++]=ae->breakpoint[i].bank;
+ brkschunk[idx++]=0;
+ brkschunk[idx++]=0;
+ }
+
+ idx-=8;
+ brkschunk[4]=idx&0xFF;
+ brkschunk[5]=(idx>>8)&0xFF;
+ brkschunk[6]=(idx>>16)&0xFF;
+ brkschunk[7]=(idx>>24)&0xFF;
+ FileWriteBinary(TMP_filename,(char*)brkschunk,idx+8); // 8 bytes for the chunk header
+ MemFree(brkschunk);
+
+
+ /* BRKC chunk for ACE emulator
+ minimal integration
+ */
+ brkschunk=MemMalloc(ae->ibreakpoint*256);
+ strcpy((char *)brkschunk,"BRKC");
+ idx=8;
+
+ for (i=0;i<ae->ibreakpoint;i++) {
+ brkschunk[idx++]=0; /* 0:Execution */
+ brkschunk[idx++]=0;
+ brkschunk[idx++]=0;
+ brkschunk[idx++]=0;
+ brkschunk[idx++]=ae->breakpoint[i].address&0xFF;
+ brkschunk[idx++]=(ae->breakpoint[i].address&0xFF00)/256;
+ for (j=0;j<2+1+1+2+4+128;j++) {
+ brkschunk[idx++]=0;
+ }
+ sprintf((char *)brkschunk+idx,"breakpoint%d",i); /* breakpoint user name? */
+ idx+=64+8;
+ }
+ idx-=8;
+ brkschunk[4]=idx&0xFF;
+ brkschunk[5]=(idx>>8)&0xFF;
+ brkschunk[6]=(idx>>16)&0xFF;
+ brkschunk[7]=(idx>>24)&0xFF;
+ FileWriteBinary(TMP_filename,(char *)brkschunk,idx+8); // 8 bytes for the chunk header
+ MemFree(brkschunk);
+ }
+ /* export optionnel des symboles */
+ if (ae->export_sna) {
+ /* SYMB chunk for ACE emulator
+
+ 1 byte - name size
+ n bytes - name (without 0 to end the string)
+ 6 bytes - reserved for future use
+ 2 bytes - shitty big endian adress for the symbol
+ */
+
+ unsigned char *symbchunk=NULL;
+ unsigned int idx=8;
+ int symbol_len;
+
+ symbchunk=MemMalloc(8+ae->il*(1+255+6+2));
+ strcpy((char *)symbchunk,"SYMB");
+
+ for (i=0;i<ae->il;i++) {
+ if (!ae->label[i].name) {
+ symbol_len=strlen(ae->wl[ae->label[i].iw].w);
+ if (symbol_len>255) symbol_len=255;
+ symbchunk[idx++]=symbol_len;
+ memcpy(symbchunk+idx,ae->wl[ae->label[i].iw].w,symbol_len);
+ idx+=symbol_len;
+ memset(symbchunk+idx,0,6);
+ idx+=6;
+ symbchunk[idx++]=(ae->label[i].ptr&0xFF00)/256;
+ symbchunk[idx++]=ae->label[i].ptr&0xFF;
+ } else {
+ if (ae->export_local || !ae->label[i].local) {
+ symbol_len=strlen(ae->label[i].name);
+ if (symbol_len>255) symbol_len=255;
+ symbchunk[idx++]=symbol_len;
+ memcpy(symbchunk+idx,ae->label[i].name,symbol_len);
+ idx+=symbol_len;
+ memset(symbchunk+idx,0,6);
+ idx+=6;
+ symbchunk[idx++]=(ae->label[i].ptr&0xFF00)/256;
+ symbchunk[idx++]=ae->label[i].ptr&0xFF;
+ }
+ }
+ }
+ if (ae->export_var) {
+ unsigned char *subchunk=NULL;
+ int retidx=0;
+ /* var are part of fast tree search structure */
+ subchunk=SnapshotDicoTree(ae,&retidx);
+ if (retidx) {
+ symbchunk=MemRealloc(symbchunk,idx+retidx);
+ memcpy(symbchunk+idx,subchunk,retidx);
+ idx+=retidx;
+ SnapshotDicoInsert("FREE",0,&retidx);
+ }
+ }
+ if (ae->export_equ) {
+ symbchunk=MemRealloc(symbchunk,idx+ae->ialias*(1+255+6+2));
+
+ for (i=0;i<ae->ialias;i++) {
+ int tmpptr;
+ symbol_len=strlen(ae->alias[i].alias);
+ if (symbol_len>255) symbol_len=255;
+ symbchunk[idx++]=symbol_len;
+ memcpy(symbchunk+idx,ae->alias[i].alias,symbol_len);
+ idx+=symbol_len;
+ memset(symbchunk+idx,0,6);
+ idx+=6;
+ tmpptr=RoundComputeExpression(ae,ae->alias[i].translation,0,0,0);
+ symbchunk[idx++]=(tmpptr&0xFF00)/256;
+ symbchunk[idx++]=tmpptr&0xFF;
+ }
+ }
+ idx-=8;
+ symbchunk[4]=idx&0xFF;
+ symbchunk[5]=(idx>>8)&0xFF;
+ symbchunk[6]=(idx>>16)&0xFF;
+ symbchunk[7]=(idx>>24)&0xFF;
+ FileWriteBinary(TMP_filename,(char*)symbchunk,idx+8); // 8 bytes for the chunk header
+ }
+ } else {
+ if (ae->export_snabrk) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"Warning: breakpoint export is not supported with snapshot version 2\n");
+ }
+ if (ae->export_sna) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"Warning: symbol export is not supported with snapshot version 2\n");
+ }
+ }
+
+ FileWriteBinaryClose(TMP_filename);
+ maxrom=(maxrom>>2)*4+4;
+ rasm_printf(ae,KAYGREEN"Total %d bank%s (%dK)\n",maxrom,maxrom>1?"s":"",(maxrom)*16);
+ }
+ }
+ /*********************************************
+ **********************************************
+ B I N A R Y F I L E
+ **********************************************
+ *********************************************/
+ } else {
+ int lastspaceid=-1;
+
+ if (ae->binary_name) {
+ sprintf(TMP_filename,"%s",ae->binary_name);
+ } else {
+ sprintf(TMP_filename,"%s.bin",ae->outputfilename);
+ }
+ FileRemoveIfExists(TMP_filename);
+
+ /* en mode binaire classique on va recherche le dernier espace mémoire dans lequel on a travaillé qui n'est pas en 'nocode' */
+ for (i=0;i<ae->io;i++) {
+ /* uniquement si le ORG a ete suivi d'ecriture */
+ if (ae->orgzone[i].memstart!=ae->orgzone[i].memend && ae->orgzone[i].nocode!=1) {
+ lastspaceid=ae->orgzone[i].ibank;
+ }
+ }
+ if (lastspaceid!=-1) {
+ for (i=0;i<ae->io;i++) {
+ if (ae->orgzone[i].protect) continue; /* protected zones exclusion */
+ /* uniquement si le ORG a ete suivi d'ecriture et n'est pas en 'nocode' */
+ if (ae->orgzone[i].ibank==lastspaceid && ae->orgzone[i].memstart!=ae->orgzone[i].memend && ae->orgzone[i].nocode!=1) {
+ if (ae->orgzone[i].memstart<minmem) minmem=ae->orgzone[i].memstart;
+ if (ae->orgzone[i].memend>maxmem) maxmem=ae->orgzone[i].memend;
+ }
+ }
+ }
+ if (maxmem-minmem<=0) {
+ if (!ae->stop) {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"Warning: Not a single byte to output\n");
+ }
+ if (ae->flux) {
+ *lenout=0;
+ }
+ } else {
+ if (!ae->flux) {
+ rasm_printf(ae,KIO"Write binary file %s (%d byte%s)\n",TMP_filename,maxmem-minmem,maxmem-minmem>1?"s":"");
+ if (ae->amsdos) {
+ AmsdosHeader=MakeAMSDOSHeader(minmem,minmem,maxmem,TMP_filename); //@@TODO
+ FileWriteBinary(TMP_filename,(char *)AmsdosHeader,128);
+ }
+ if (maxmem-minmem>0) {
+ FileWriteBinary(TMP_filename,(char*)ae->mem[lastspaceid]+minmem,maxmem-minmem);
+ FileWriteBinaryClose(TMP_filename);
+ } else {
+ if (ae->amsdos) {
+ FileWriteBinaryClose(TMP_filename);
+ }
+ }
+ } else {
+ *dataout=MemMalloc(maxmem-minmem+1);
+ memcpy(*dataout,ae->mem[lastspaceid]+minmem,maxmem-minmem);
+ *lenout=maxmem-minmem;
+ }
+ }
+ }
+ }
+ /********************************
+ *********************************
+ U N U S E D W A R N I N G
+ *********************************
+ ********************************/
+ if (ae->warn_unused) {
+ for (i=0;i<ae->ialias;i++) {
+ if (strcmp(ae->alias[i].alias,"IX") && strcmp(ae->alias[i].alias,"IY")) {
+ if (!ae->alias[i].used) {
+ rasm_printf(ae,KWARNING"[%s:%d] Warning: alias %s declared but not used\n",ae->filename[ae->wl[ae->alias[i].iw].ifile],ae->wl[ae->alias[i].iw].l,ae->alias[i].alias);
+ }
+ }
+ }
+ WarnLabelTree(ae);
+ WarnDicoTree(ae);
+ }
+
+ /****************************
+ *****************************
+ S Y M B O L E X P O R T
+ *****************************
+ ****************************/
+ if (ae->export_sym && !ae->export_sna) {
+ char *SymbolFileName;
+ SymbolFileName=MemMalloc(PATH_MAX);
+
+#define MAKE_SYMBOL_NAME if (ae->symbol_name) {sprintf(TMP_filename,"%s",ae->symbol_name);} else {sprintf(TMP_filename,"%s.sym",ae->outputfilename);}
+
+ MAKE_SYMBOL_NAME
+ FileRemoveIfExists(TMP_filename);
+
+ if (ae->export_multisym) {
+ /* multi-remove before writes */
+ for (i=0;i<ae->nbbank;i++) {
+ if (ae->symbol_name) {
+ sprintf(TMP_filename,"%s.bank%d",ae->symbol_name,i);
+ } else {
+ sprintf(TMP_filename,"%s.sym.bank%d",ae->outputfilename,i);
+ }
+ FileRemoveIfExists(TMP_filename);
+ }
+ rasm_printf(ae,KIO"Write symbol files %s.bank*\n",TMP_filename);
+ } else {
+ rasm_printf(ae,KIO"Write symbol file %s\n",TMP_filename);
+ }
+
+ switch (ae->export_sym) {
+ case 5:
+ /* ZX export */
+ for (i=0;i<ae->il;i++) {
+ if (ae->label[i].autorise_export) {
+ if (ae->export_multisym) {
+ if (ae->symbol_name) {
+ sprintf(TMP_filename,"%s.bank%d",ae->symbol_name,ae->label[i].ibank);
+ } else {
+ sprintf(TMP_filename,"%s.sym.bank%d",ae->outputfilename,ae->label[i].ibank);
+ }
+ }
+
+ if (!ae->label[i].name) {
+ sprintf(symbol_line,"%d:%04X %s\n",ae->label[i].ibank,ae->label[i].ptr,ae->wl[ae->label[i].iw].w);
+ FileWriteLine(TMP_filename,symbol_line);
+ } else {
+ if (ae->export_local || !ae->label[i].local) {
+ sprintf(symbol_line,"%d:%04X %s\n",ae->label[i].ibank,ae->label[i].ptr,ae->label[i].name);
+ FileWriteLine(TMP_filename,symbol_line);
+ }
+ }
+ }
+ }
+ FileWriteLineClose(TMP_filename);
+ MAKE_SYMBOL_NAME
+ if (ae->export_var) {
+ /* var are part of fast tree search structure */
+ ExportDicoTree(ae,TMP_filename,"%s %04X");
+ }
+ if (ae->export_equ) {
+ for (i=0;i<ae->ialias;i++) {
+ if (strcmp(ae->alias[i].alias,"IX") && strcmp(ae->alias[i].alias,"IY")) {
+ sprintf(symbol_line,"%04X %s\n",RoundComputeExpression(ae,ae->alias[i].translation,0,-ae->alias[i].iw,0),ae->alias[i].alias);
+ FileWriteLine(TMP_filename,symbol_line);
+ }
+ }
+ }
+ break;
+ case 4:
+ /* flexible */
+ for (i=0;i<ae->il;i++) {
+ if (ae->label[i].autorise_export) {
+ if (ae->export_multisym) {
+ if (ae->symbol_name) {
+ sprintf(TMP_filename,"%s.bank%d",ae->symbol_name,ae->label[i].ibank);
+ } else {
+ sprintf(TMP_filename,"%s.sym.bank%d",ae->outputfilename,ae->label[i].ibank);
+ }
+ }
+
+ if (!ae->label[i].name) {
+ sprintf(symbol_line,ae->flexible_export,ae->wl[ae->label[i].iw].w,ae->label[i].ptr);
+ FileWriteLine(TMP_filename,symbol_line);
+ } else {
+ if (ae->export_local || !ae->label[i].local) {
+ sprintf(symbol_line,ae->flexible_export,ae->label[i].name,ae->label[i].ptr);
+ FileWriteLine(TMP_filename,symbol_line);
+ }
+ }
+ }
+ }
+ MAKE_SYMBOL_NAME
+ if (ae->export_var) {
+ /* var are part of fast tree search structure */
+ ExportDicoTree(ae,TMP_filename,ae->flexible_export);
+ }
+ if (ae->export_equ) {
+ for (i=0;i<ae->ialias;i++) {
+ if (strcmp(ae->alias[i].alias,"IX") && strcmp(ae->alias[i].alias,"IY")) {
+ sprintf(symbol_line,ae->flexible_export,ae->alias[i].alias,RoundComputeExpression(ae,ae->alias[i].translation,0,-ae->alias[i].iw,0));
+ FileWriteLine(TMP_filename,symbol_line);
+ }
+ }
+ }
+ FileWriteLineClose(TMP_filename);
+ break;
+ case 3:
+ /* winape */
+ for (i=0;i<ae->il;i++) {
+ if (ae->label[i].autorise_export) {
+ if (ae->export_multisym) {
+ if (ae->symbol_name) {
+ sprintf(TMP_filename,"%s.bank%d",ae->symbol_name,ae->label[i].ibank);
+ } else {
+ sprintf(TMP_filename,"%s.sym.bank%d",ae->outputfilename,ae->label[i].ibank);
+ }
+ }
+ if (!ae->label[i].name) {
+ sprintf(symbol_line,"%s #%04X\n",ae->wl[ae->label[i].iw].w,ae->label[i].ptr);
+ FileWriteLine(TMP_filename,symbol_line);
+ } else {
+ if (ae->export_local || !ae->label[i].local) {
+ sprintf(symbol_line,"%s #%04X\n",ae->label[i].name,ae->label[i].ptr);
+ FileWriteLine(TMP_filename,symbol_line);
+ }
+ }
+ }
+ }
+ MAKE_SYMBOL_NAME
+ if (ae->export_var) {
+ /* var are part of fast tree search structure */
+ ExportDicoTree(ae,TMP_filename,"%s #%04X\n");
+ }
+ if (ae->export_equ) {
+ for (i=0;i<ae->ialias;i++) {
+ if (strcmp(ae->alias[i].alias,"IX") && strcmp(ae->alias[i].alias,"IY")) {
+ sprintf(symbol_line,"%s #%04X\n",ae->alias[i].alias,RoundComputeExpression(ae,ae->alias[i].translation,0,-ae->alias[i].iw,0));
+ FileWriteLine(TMP_filename,symbol_line);
+ }
+ }
+ }
+ FileWriteLineClose(TMP_filename);
+ break;
+ case 2:
+ /* pasmo */
+ for (i=0;i<ae->il;i++) {
+ if (ae->label[i].autorise_export) {
+ if (ae->export_multisym) {
+ if (ae->symbol_name) {
+ sprintf(TMP_filename,"%s.bank%d",ae->symbol_name,ae->label[i].ibank);
+ } else {
+ sprintf(TMP_filename,"%s.sym.bank%d",ae->outputfilename,ae->label[i].ibank);
+ }
+ }
+ if (!ae->label[i].name) {
+ sprintf(symbol_line,"%s EQU 0%04XH\n",ae->wl[ae->label[i].iw].w,ae->label[i].ptr);
+ FileWriteLine(TMP_filename,symbol_line);
+ } else {
+ if (ae->export_local || !ae->label[i].local) {
+ sprintf(symbol_line,"%s EQU 0%04XH\n",ae->label[i].name,ae->label[i].ptr);
+ FileWriteLine(TMP_filename,symbol_line);
+ }
+ }
+ }
+ }
+ MAKE_SYMBOL_NAME
+ if (ae->export_var) {
+ /* var are part of fast tree search structure */
+ ExportDicoTree(ae,TMP_filename,"%s EQU 0%04XH\n");
+ }
+ if (ae->export_equ) {
+ for (i=0;i<ae->ialias;i++) {
+ if (strcmp(ae->alias[i].alias,"IX") && strcmp(ae->alias[i].alias,"IY")) {
+ sprintf(symbol_line,"%s EQU 0%04XH\n",ae->alias[i].alias,RoundComputeExpression(ae,ae->alias[i].translation,0,-ae->alias[i].iw,0));
+ FileWriteLine(TMP_filename,symbol_line);
+ }
+ }
+ }
+ FileWriteLineClose(TMP_filename);
+ break;
+ case 1:
+ /* Rasm */
+ for (i=0;i<ae->il;i++) {
+ if (ae->label[i].autorise_export) {
+ if (ae->export_multisym) {
+ if (ae->symbol_name) {
+ sprintf(TMP_filename,"%s.bank%d",ae->symbol_name,ae->label[i].ibank);
+ } else {
+ sprintf(TMP_filename,"%s.sym.bank%d",ae->outputfilename,ae->label[i].ibank);
+ }
+ }
+ if (!ae->label[i].name) {
+ sprintf(symbol_line,"%s #%X B%d\n",ae->wl[ae->label[i].iw].w,ae->label[i].ptr,ae->label[i].ibank>31?0:ae->label[i].ibank);
+ FileWriteLine(TMP_filename,symbol_line);
+ } else {
+ if (ae->export_local) {
+ sprintf(symbol_line,"%s #%X B%d\n",ae->label[i].name,ae->label[i].ptr,ae->label[i].ibank>31?0:ae->label[i].ibank);
+ FileWriteLine(TMP_filename,symbol_line);
+ }
+ }
+ }
+ }
+ MAKE_SYMBOL_NAME
+ if (ae->export_var) {
+ /* var are part of fast tree search structure */
+ ExportDicoTree(ae,TMP_filename,"%s #%X B0\n");
+ }
+ if (ae->export_equ) {
+ for (i=0;i<ae->ialias;i++) {
+ if (strcmp(ae->alias[i].alias,"IX") && strcmp(ae->alias[i].alias,"IY") && ae->alias[i].autorise_export) {
+ sprintf(symbol_line,"%s #%X B0\n",ae->alias[i].alias,RoundComputeExpression(ae,ae->alias[i].translation,0,-ae->alias[i].iw,0));
+ FileWriteLine(TMP_filename,symbol_line);
+ }
+ }
+ }
+ FileWriteLineClose(TMP_filename);
+ break;
+ case 0:
+ default:break;
+ }
+ MemFree(SymbolFileName);
+ }
+ /*********************************
+ **********************************
+ B R E A K P O I N T S
+ **********************************
+ *********************************/
+ if (ae->export_brk) {
+ struct s_breakpoint breakpoint={0};
+
+ if (ae->breakpoint_name) {
+ sprintf(TMP_filename,"%s",ae->breakpoint_name);
+ } else {
+ sprintf(TMP_filename,"%s.brk",ae->outputfilename);
+ }
+ FileRemoveIfExists(TMP_filename);
+
+ /* add labels and local labels to breakpoint pool (if any) */
+ for (i=0;i<ae->il;i++) {
+ if (!ae->label[i].name) {
+ if (strncmp(ae->wl[ae->label[i].iw].w,"BRK",3)==0) {
+ breakpoint.address=ae->label[i].ptr;
+ if (ae->label[i].ibank>3) breakpoint.bank=1; else breakpoint.bank=0;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->breakpoint,&ae->ibreakpoint,&ae->maxbreakpoint,&breakpoint,sizeof(struct s_breakpoint));
+ }
+ } else {
+ if (strncmp(ae->label[i].name,"@BRK",4)==0) {
+ breakpoint.address=ae->label[i].ptr;
+ if (ae->label[i].ibank>3) breakpoint.bank=1; else breakpoint.bank=0;
+ ObjectArrayAddDynamicValueConcat((void **)&ae->breakpoint,&ae->ibreakpoint,&ae->maxbreakpoint,&breakpoint,sizeof(struct s_breakpoint));
+ }
+ }
+ }
+
+ if (ae->ibreakpoint) {
+ rasm_printf(ae,KIO"Write breakpoint file %s\n",TMP_filename);
+ for (i=0;i<ae->ibreakpoint;i++) {
+ sprintf(symbol_line,"#%04X\n",ae->breakpoint[i].address);
+ FileWriteLine(TMP_filename,symbol_line);
+ }
+ FileWriteLineClose(TMP_filename);
+ } else {
+ if (!ae->nowarning) rasm_printf(ae,KWARNING"Warning: no breakpoint to output (previous file [%s] deleted anyway)\n",TMP_filename);
+ }
+ }
+
+ } else {
+ if (!ae->dependencies) rasm_printf(ae,KERROR"%d error%s\n",ae->nberr,ae->nberr>1?"s":"");
+ }
+#if TRACE_ASSEMBLE
+printf("dependencies\n");
+#endif
+/*******************************************************************************************
+ E X P O R T D E P E N D E N C I E S
+*******************************************************************************************/
+ if (ae->dependencies) {
+ int trigdep=0;
+
+ /* depends ALL */
+ if (ae->outputfilename && strcmp(ae->outputfilename,"rasmoutput")) {
+ trigdep=1;
+ printf("%s",ae->outputfilename);
+ if (ae->dependencies==E_DEPENDENCIES_MAKE) printf(" "); else printf("\n");
+ }
+ for (i=1;i<ae->ifile;i++) {
+ trigdep=1;
+ SimplifyPath(ae->filename[i]);
+ printf("%s",ae->filename[i]);
+ if (ae->dependencies==E_DEPENDENCIES_MAKE) printf(" "); else printf("\n");
+ }
+ for (i=0;i<ae->ih;i++) {
+ trigdep=1;
+ SimplifyPath(ae->hexbin[i].filename);
+ printf("%s",ae->hexbin[i].filename);
+ if (ae->dependencies==E_DEPENDENCIES_MAKE) printf(" "); else printf("\n");
+ }
+ if (ae->dependencies==E_DEPENDENCIES_MAKE && trigdep) printf("\n");
+ }
+
+/*******************************************************************************************
+ V E R B O S E S H I T
+*******************************************************************************************/
+#if TRACE_ASSEMBLE
+ rasm_printf(ae,KVERBOSE"------ statistics ------------------\n");
+ rasm_printf(ae,KVERBOSE"%d file%s\n",ae->ifile,ae->ifile>1?"s":"");
+ rasm_printf(ae,KVERBOSE"%d binary include%s\n",ae->ih,ae->ih>1?"s":"");
+ rasm_printf(ae,KVERBOSE"%d word%s\n",ae->nbword-1,ae->nbword>2?"s":"");
+ rasm_printf(ae,KVERBOSE"%d label%s\n",ae->il,ae->il>1?"s":"");
+ rasm_printf(ae,KVERBOSE"%d struct%s\n",ae->irasmstruct,ae->irasmstruct>1?"s":"");
+ rasm_printf(ae,KVERBOSE"%d var%s\n",ae->idic,ae->idic>1?"s":"");
+ rasm_printf(ae,KVERBOSE"%d expression%s\n",ae->ie,ae->ie>1?"s":"");
+ rasm_printf(ae,KVERBOSE"%d macro%s\n",ae->imacro,ae->imacro>1?"s":"");
+ rasm_printf(ae,KVERBOSE"%d alias%s\n",ae->ialias,ae->ialias>1?"s":"");
+ rasm_printf(ae,KVERBOSE"%d ORG zone%s\n",ae->io-1,ae->io>2?"s":"");
+ rasm_printf(ae,KVERBOSE"%d virtual space%s\n",ae->nbbank,ae->nbbank>1?"s":"");
+#endif
+
+/*******************************************************************************************
+ C L E A N U P
+*******************************************************************************************/
+#if TRACE_ASSEMBLE
+printf("cleanup\n");
+#endif
+#if TRACE_GENERALE
+printf("-cleanup\n");
+#endif
+ if (TMP_filename) MemFree(TMP_filename);
+ if (ae->nberr) {
+ ok=-1;
+ if (ae->flux && *dataout) {
+ MemFree(*dataout);
+ *dataout=NULL;
+ }
+ if (lenout) *lenout=0;
+ } else {
+ ok=0;
+ }
+
+ FreeAssenv(ae);
+#if TRACE_ASSEMBLE
+printf("end of assembling\n");
+#endif
+#if TRACE_GENERALE
+printf("-end ok=%d\n",ok);
+#endif
+ return ok;
+}
+
+
+void EarlyPrepSrc(struct s_assenv *ae, char **listing, char *filename) {
+ int l,idx,c,quote_type=0;
+ int mlc_start,mlc_idx;
+
+ /* virer les commentaires en ;, // mais aussi multi-lignes et convertir les decalages, passer les chars en upper case */
+ l=idx=0;
+ while (listing[l]) {
+ c=listing[l][idx++];
+
+ if (!c) {
+ l++;
+ idx=0;
+ continue;
+ } else if (!quote_type) {
+ /* upper case */
+ if (c>='a' && c<='z') {
+ listing[l][idx-1]=c=c-'a'+'A';
+ }
+
+ if (c=='\'' && idx>2 && strncmp(&listing[l][idx-3],"AF'",3)==0) {
+ /* il ne faut rien faire */
+ } else if (c=='"' || c=='\'') {
+ quote_type=c;
+ } else if (c==';' || (c=='/' && listing[l][idx]=='/')) {
+ idx--;
+ while (listing[l][idx] && listing[l][idx]!=0x0D && listing[l][idx]!=0x0A) listing[l][idx++]=':';
+ idx--;
+ } else if (c=='>' && listing[l][idx]=='>' && !quote_type) {
+ listing[l][idx-1]=']';
+ listing[l][idx++]=' ';
+ continue;
+ } else if (c=='<' && listing[l][idx]=='<' && !quote_type) {
+ listing[l][idx-1]='[';
+ listing[l][idx++]=' ';
+ continue;
+ } else if (c=='/' && listing[l][idx]=='*' && !quote_type) {
+ /* multi-line comment */
+ mlc_start=l;
+ mlc_idx=idx-1;
+ idx++;
+ while (1) {
+ c=listing[l][idx++];
+ if (!c) {
+ idx=0;
+ l++;
+ if (!listing[l]) {
+ MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"opened comment to the end of the file\n",filename,l+1);
+ return;
+ }
+ } else if (c=='*' && listing[l][idx]=='/') {
+ idx++;
+ break;
+ }
+ }
+ /* merge */
+ if (mlc_start==l) {
+ /* on the same line */
+ while (mlc_idx<idx) listing[l][mlc_idx++]=' '; /* raz with spaces */
+ } else {
+ /* multi-line */
+ listing[mlc_start][mlc_idx]=0; /* raz EOL */
+ mlc_start++;
+ while (mlc_start<l) listing[mlc_start++][0]=0; /* raz line */
+ mlc_idx=0;
+ while (mlc_idx<idx) listing[l][mlc_idx++]=' '; /* raz beginning of the line */
+ }
+ }
+ } else {
+ /* in quote */
+ if (c=='\\') {
+ if (listing[l][idx]) {
+ idx++;
+ }
+ } else if (c==quote_type) {
+ quote_type=0;
+ }
+ }
+ }
+}
+
+void PreProcessingSplitListing(struct s_listing **listing, int *il, int *ml, int idx, int end, int start)
+{
+ #undef FUNC
+ #define FUNC "PreProcessingSplitListing"
+
+ struct s_listing curlisting={0};
+
+ /* split current line because there will be "before" include and "after include" line */
+ ObjectArrayAddDynamicValueConcat((void**)listing,il,ml,&curlisting,sizeof(curlisting));
+ MemMove(&((*listing)[idx+2]),&((*listing)[idx+1]),(*il-idx-2)*sizeof(struct s_listing));
+ (*listing)[idx+1].ifile=(*listing)[idx].ifile;
+ (*listing)[idx+1].iline=(*listing)[idx].iline;
+ if ((*listing)[idx].listing[start]) {
+ (*listing)[idx+1].listing=TxtStrDup((*listing)[idx].listing+start);
+ } else {
+ (*listing)[idx+1].listing=TxtStrDup(";");
+ }
+ strcpy((*listing)[idx].listing+end,":");
+}
+
+void PreProcessingInsertListing(struct s_listing **reflisting, int *il, int *ml, int idx, char **zelines, int ifile)
+{
+ #undef FUNC
+ #define FUNC "PreProcessingSplitListing"
+
+ struct s_listing *listing;
+ int nbinsert,li,bil;
+ for (li=nbinsert=0;zelines[li];li++) nbinsert++;
+ bil=*il;
+ if (*il+nbinsert>=*ml) {
+ *il=*ml=*il+nbinsert;
+ *reflisting=MemRealloc(*reflisting,sizeof(struct s_listing)*(*ml));
+ } else {
+ *il=*il+nbinsert;
+ }
+ listing=*reflisting;
+ MemMove(&listing[idx+1+nbinsert],&listing[idx+1],(bil-idx-1)*sizeof(struct s_listing));
+
+ for (li=0;zelines[li];li++) {
+ listing[idx+1+li].ifile=ifile;
+ listing[idx+1+li].iline=li+1;
+ listing[idx+1+li].listing=zelines[li];
+ }
+}
+
+int cmpkeyword(const void * a, const void * b)
+{
+ struct s_asm_keyword *sa,*sb;
+ sa=(struct s_asm_keyword *)a;
+ sb=(struct s_asm_keyword *)b;
+ return strcmp(sa->mnemo,sb->mnemo);
+}
+
+struct s_assenv *PreProcessing(char *filename, int flux, const char *datain, int datalen, struct s_parameter *param)
+{
+ #undef FUNC
+ #define FUNC "PreProcessing"
+
+ #define CharWord "@ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.=_($)][+-*/^%#|&'\"\\m}{[]"
+
+ struct s_assenv *ae=NULL;
+ struct s_wordlist curw={0};
+ struct s_wordlist *wordlist=NULL;
+ int nbword=0,maxword=0;
+ char **zelines=NULL;
+
+ char *filename_toread;
+
+ struct s_macro_fast *MacroFast=NULL;
+ int idxmacrofast=0,maxmacrofast=0;
+
+ struct s_listing *listing=NULL;
+ struct s_listing curlisting;
+ int ilisting=0,maxlisting=0;
+
+ char **listing_include=NULL;
+ int i,j,l=0,idx=0,c=0,li,le;
+ char Automate[256]={0};
+ struct s_hexbin curhexbin;
+ char *newlistingline=NULL;
+ unsigned char *newdata;
+ struct s_label curlabel={0};
+ char *labelsep1;
+ char **labelines=NULL;
+ /* state machine buffer */
+ unsigned char *mem=NULL;
+ char *w=NULL,*wtmp=NULL;
+ int lw=0,mw=256;
+ char *bval=NULL;
+ int ival=0,sval=256;
+ char *qval=NULL;
+ int iqval=0,sqval=256;
+ struct s_repeat_index *TABrindex=NULL;
+ struct s_repeat_index *TABwindex=NULL;
+ struct s_repeat_index rindex={0};
+ struct s_repeat_index windex={0};
+ int nri=0,mri=0,ri=0;
+ int nwi=0,mwi=0,wi=0;
+ /* state machine trigger */
+ int waiting_quote=0,lquote;
+ int macro_trigger=0;
+ int escape_code=0;
+ int quote_type=0;
+ int incbin=0,include=0,crunch=0;
+ int rewrite=0,hadcomma=0;
+ int nbinstruction;
+ int ifast,texpr;
+ int ispace=0;
+
+#if TRACE_GENERALE
+printf("*** preprocessing ***\n");
+#endif
+
+#if TRACE_PREPRO
+printf("start prepro, alloc assenv\n");
+#endif
+
+ windex.cl=-1;
+ windex.cidx=-1;
+ rindex.cl=-1;
+ rindex.cidx=-1;
+
+#if TRACE_PREPRO
+printf("malloc\n");
+#endif
+ ae=MemMalloc(sizeof(struct s_assenv));
+#if TRACE_PREPRO
+printf("memset\n");
+#endif
+ memset(ae,0,sizeof(struct s_assenv));
+
+#if TRACE_PREPRO
+printf("paramz 1\n");
+#endif
+ if (param) {
+ ae->export_local=param->export_local;
+ ae->export_sym=param->export_sym;
+ ae->export_var=param->export_var;
+ ae->export_equ=param->export_equ;
+ ae->export_sna=param->export_sna;
+ ae->export_snabrk=param->export_snabrk;
+ if (param->export_sna || param->export_snabrk) {
+ ae->forcesnapshot=1;
+ }
+ ae->export_brk=param->export_brk;
+ ae->warn_unused=param->warn_unused;
+ ae->edskoverwrite=param->edskoverwrite;
+ ae->rough=param->rough;
+ ae->as80=param->as80;
+ ae->dams=param->dams;
+ ae->macrovoid=param->macrovoid;
+ if (param->v2) {
+ ae->forcesnapshot=1;
+ ae->snapshot.version=2;
+ } else {
+ ae->snapshot.version=3;
+ }
+ ae->maxerr=param->maxerr;
+ ae->extended_error=param->extended_error;
+ ae->nowarning=param->nowarning;
+ ae->breakpoint_name=param->breakpoint_name;
+ ae->symbol_name=param->symbol_name;
+ ae->binary_name=param->binary_name;
+ ae->flexible_export=param->flexible_export;
+ ae->cartridge_name=param->cartridge_name;
+ ae->snapshot_name=param->snapshot_name;
+ ae->checkmode=param->checkmode;
+ if (param->rough) ae->maxam=0; else ae->maxam=1;
+ /* additional symbols */
+ for (i=0;i<param->nsymb;i++) {
+ char *sep;
+ sep=strchr(param->symboldef[i],'=');
+ if (sep) {
+ *sep=0;
+ ExpressionSetDicoVar(ae,param->symboldef[i],atof(sep+1));
+ }
+ }
+ if (param->msymb) {
+ MemFree(param->symboldef);
+ param->nsymb=param->msymb=0;
+ }
+ /* include paths */
+ ae->includepath=param->pathdef;
+ ae->ipath=param->npath;
+ ae->mpath=param->mpath;
+ /* old inline params */
+ ae->dependencies=param->dependencies;
+ }
+#if TRACE_PREPRO
+printf("init 0\n");
+#endif
+#if TRACE_GENERALE
+printf("-init\n");
+#endif
+ /* generic init */
+ ae->ctx1.maxivar=1;
+ ae->ctx2.maxivar=1;
+ ae->computectx=&ae->ctx1;
+ ae->flux=flux;
+ /* check snapshot structure */
+ if (sizeof(ae->snapshot)!=0x100 || &ae->snapshot.fdd.motorstate-(unsigned char*)&ae->snapshot!=0x9C || &ae->snapshot.crtcstate.model-(unsigned char*)&ae->snapshot!=0xA4
+ || &ae->snapshot.romselect-(unsigned char*)&ae->snapshot!=0x55
+ || &ae->snapshot.interruptrequestflag-(unsigned char*)&ae->snapshot!=0xB4
+ || &ae->snapshot.CPCType-(unsigned char*)&ae->snapshot!=0x6D) {
+ rasm_printf(ae,"snapshot structure integrity check KO\n");
+ exit(349);
+ }
+
+ for (i=0;i<4;i++) {
+ ae->bankgate[i]=0x7FC0; /* video memory has no paging */
+ ae->setgate[i]=0x7FC0; /* video memory has no paging */
+ }
+ for (i=0;i<256;i++) {
+ /* 4M expansion support on lower gate array port */
+ ae->bankgate[i+4]=0x7FC4+(i&3)+((i&31)>>2)*8-0x100*(i>>5);
+ ae->setgate[i+4] =0x7FC2 +((i&31)>>2)*8-0x100*(i>>5);
+ //printf("%04X %04X\n",ae->bankgate[i+4],ae->setgate[i+4]);
+ }
+
+ memcpy(ae->snapshot.idmark,"MV - SNA",8);
+ ae->snapshot.registers.IM=1;
+
+ ae->snapshot.gatearray.palette[0]=0x04;
+ ae->snapshot.gatearray.palette[1]=0x0A;
+ ae->snapshot.gatearray.palette[2]=0x15;
+ ae->snapshot.gatearray.palette[3]=0x1C;
+ ae->snapshot.gatearray.palette[4]=0x18;
+ ae->snapshot.gatearray.palette[5]=0x1D;
+ ae->snapshot.gatearray.palette[6]=0x0C;
+ ae->snapshot.gatearray.palette[7]=0x05;
+ ae->snapshot.gatearray.palette[8]=0x0D;
+ ae->snapshot.gatearray.palette[9]=0x16;
+ ae->snapshot.gatearray.palette[10]=0x06;
+ ae->snapshot.gatearray.palette[11]=0x17;
+ ae->snapshot.gatearray.palette[12]=0x1E;
+ ae->snapshot.gatearray.palette[13]=0x00;
+ ae->snapshot.gatearray.palette[14]=0x1F;
+ ae->snapshot.gatearray.palette[15]=0x0E;
+ ae->snapshot.gatearray.palette[16]=0x04;
+
+ ae->snapshot.gatearray.multiconfiguration=0x8D; // lower/upper ROM off + mode 1
+ ae->snapshot.CPCType=2; /* 6128 */
+ ae->snapshot.crtcstate.model=0; /* CRTC 0 */
+ ae->snapshot.vsyncdelay=2;
+ strcpy((char *)ae->snapshot.unused6+3+0x20+8,RASM_VERSION);
+ /* CRTC default registers */
+ ae->snapshot.crtc.registervalue[0]=0x3F;
+ ae->snapshot.crtc.registervalue[1]=40;
+ ae->snapshot.crtc.registervalue[2]=46;
+ ae->snapshot.crtc.registervalue[3]=0x8E;
+ ae->snapshot.crtc.registervalue[4]=38;
+ ae->snapshot.crtc.registervalue[6]=25;
+ ae->snapshot.crtc.registervalue[7]=30;
+ ae->snapshot.crtc.registervalue[9]=7;
+ ae->snapshot.crtc.registervalue[12]=0x30;
+ ae->snapshot.psg.registervalue[7]=0x3F; /* audio mix all channels OFF */
+ /* PPI Init */
+ ae->snapshot.ppi.control=0x82;
+ /* standard stack */
+ ae->snapshot.registers.HSP=0xC0;
+
+ /*
+ winape sprintf(symbol_line,"%s #%4X\n",ae->label[i].name,ae->label[i].ptr);
+ pasmo sprintf(symbol_line,"%s EQU 0%4XH\n",ae->label[i].name,ae->label[i].ptr);
+ rasm sprintf(symbol_line,"%s #%X B%d\n",ae->wl[ae->label[i].iw].w,ae->label[i].ptr,ae->label[i].ibank>31?0:ae->label[i].ibank);
+ */
+#if TRACE_PREPRO
+printf("paramz\n");
+#endif
+ if (param && param->labelfilename) {
+ for (j=0;param->labelfilename[j] && param->labelfilename[j][0];j++) {
+ rasm_printf(ae,"Label import from [%s]\n",param->labelfilename[j]);
+ ae->label_filename=param->labelfilename[j];
+ ae->label_line=1;
+ labelines=FileReadLines(param->labelfilename[j]);
+ i=0;
+ while (labelines[i]) {
+ /* upper case */
+ for (j=0;labelines[i][j];j++) labelines[i][j]=toupper(labelines[i][j]);
+
+ if ((labelsep1=strstr(labelines[i],": EQU 0"))!=NULL) {
+ /* sjasm */
+ *labelsep1=0;
+ curlabel.name=labelines[i];
+ curlabel.iw=-1;
+ curlabel.crc=GetCRC(curlabel.name);
+ curlabel.ptr=strtol(labelsep1+6,NULL,16);
+ PushLabelLight(ae,&curlabel);
+ } else if ((labelsep1=strstr(labelines[i]," EQU 0"))!=NULL) {
+ /* pasmo */
+ *labelsep1=0;
+ curlabel.name=labelines[i];
+ curlabel.iw=-1;
+ curlabel.crc=GetCRC(curlabel.name);
+ curlabel.ptr=strtol(labelsep1+6,NULL,16);
+ //ObjectArrayAddDynamicValueConcat((void **)&ae->label,&ae->il,&ae->ml,&curlabel,sizeof(curlabel));
+ PushLabelLight(ae,&curlabel);
+ } else if ((labelsep1=strstr(labelines[i]," "))!=NULL) {
+ /* winape / rasm */
+ if (*(labelsep1+1)=='#') {
+ *labelsep1=0;
+ curlabel.name=labelines[i];
+ curlabel.iw=-1;
+ curlabel.crc=GetCRC(curlabel.name);
+ curlabel.ptr=strtol(labelsep1+2,NULL,16);
+ //ObjectArrayAddDynamicValueConcat((void **)&ae->label,&ae->il,&ae->ml,&curlabel,sizeof(curlabel));
+ PushLabelLight(ae,&curlabel);
+ }
+ }
+ i++;
+ ae->label_line++;
+ }
+ MemFree(labelines);
+ }
+ ae->label_filename=NULL;
+ ae->label_line=0;
+ }
+#if TRACE_PREPRO
+printf("init 3\n");
+#endif
+ /* 32 CPR default roms but 260+ max snapshot RAM pages + one workspace */
+ for (i=0;i<BANK_MAX_NUMBER+1;i++) {
+ mem=MemMalloc(65536);
+ memset(mem,0,65536);
+ ObjectArrayAddDynamicValueConcat((void**)&ae->mem,&ae->nbbank,&ae->maxbank,&mem,sizeof(mem));
+ }
+#if TRACE_PREPRO
+printf("nbbank=%d initialised\n",ae->nbbank);
+#endif
+ ae->activebank=BANK_MAX_NUMBER;
+ ae->maxptr=65536;
+ for (i=0;i<256;i++) { ae->charset[i]=(unsigned char)i; }
+
+ if (param && param->outputfilename) {
+ ae->outputfilename=TxtStrDup(param->outputfilename);
+ } else if (param && param->automatic_radix && param->filename) {
+ int rilook;
+ rilook=strlen(param->filename);
+ ae->outputfilename=TxtStrDup(param->filename);
+ /* look for extension */
+ while (rilook && ae->outputfilename[rilook]!='.') {
+ /* end of scan with directory reference or nothing found */
+ if (ae->outputfilename[rilook]=='/' || ae->outputfilename[rilook]=='\\') rilook=0; else rilook--;
+ }
+ if (ae->outputfilename[rilook]=='.') {
+ ae->outputfilename[rilook]=0;
+ }
+ } else {
+ ae->outputfilename=TxtStrDup("rasmoutput");
+ }
+ /* si on est en ligne de commande ET que le fichier n'est pas trouvé */
+ if (param && param->filename && !FileExists(param->filename)) {
+ char *LTryExtension[]={".asm",".z80",".o",".dam",".mxm",".txt",
+ ".ASM",".Z80",".O",".DAM",".MXM",".TXT",NULL};
+
+ int iguess=1;
+ l=strlen(param->filename);
+ filename=MemRealloc(param->filename,l+6);
+ /* si le nom du fichier termine par un . on n'ajoute que l'extension, sinon on l'ajoute avec le . */
+ if (param->filename[l-1]=='.') strcat(param->filename,"asm"); else strcat(param->filename,".asm");
+
+ while (!FileExists(param->filename) && LTryExtension[iguess]!=NULL) {
+ TxtReplace(param->filename,LTryExtension[iguess-1],LTryExtension[iguess],0); /* no realloc with this */
+ if (!FileExists(param->filename)) {
+ param->filename[l]=0;
+ }
+ iguess++;
+ }
+ }
+
+ if (param && param->filename && !FileExists(param->filename)) {
+ rasm_printf(ae,"Cannot find file [%s]\n",param->filename);
+ exit(-1802);
+ }
+
+ if (param) rasm_printf(ae,KAYGREEN"Pre-processing [%s]\n",param->filename);
+ for (nbinstruction=0;instruction[nbinstruction].mnemo[0];nbinstruction++);
+ qsort(instruction,nbinstruction,sizeof(struct s_asm_keyword),cmpkeyword);
+ for (i=0;i<256;i++) { ae->fastmatch[i]=-1; }
+ for (i=0;i<nbinstruction;i++) { if (ae->fastmatch[(int)instruction[i].mnemo[0]]==-1) ae->fastmatch[(int)instruction[i].mnemo[0]]=i; }
+ for (i=0;CharWord[i];i++) {Automate[((int)CharWord[i])&0xFF]=1;}
+ /* separators */
+ Automate[' ']=2;
+ Automate[',']=2;
+ Automate['\t']=2;
+ /* end of line */
+ Automate[':']=3; /* les 0x0A et 0x0D seront deja  remplaces en ':' */
+ /* expression */
+ Automate['=']=4; /* on stocke l'emplacement de l'egalite */
+ Automate['<']=4; /* ou des operateurs */
+ Automate['>']=4; /* d'evaluation */
+ Automate['!']=4;
+
+ StateMachineResizeBuffer(&w,256,&mw);
+ StateMachineResizeBuffer(&bval,256,&sval);
+ StateMachineResizeBuffer(&qval,256,&sqval);
+ w[0]=0;
+ bval[0]=0;
+ qval[0]=0;
+
+#if TRACE_PREPRO
+printf("read file/flux\n");
+#endif
+#if TRACE_GENERALE
+printf("-read/flux\n");
+#endif
+
+ if (!ae->flux) {
+ zelines=FileReadLines(filename);
+ FieldArrayAddDynamicValueConcat(&ae->filename,&ae->ifile,&ae->maxfile,filename);
+ } else {
+ int flux_nblines=0;
+ int flux_curpos;
+
+ /* copie des données */
+ for (i=0;i<datalen;i++) {
+ if (datain[i]=='\n') flux_nblines++;
+ }
+ zelines=MemMalloc(sizeof(char *)*(flux_nblines+2));
+ flux_nblines=0;
+ flux_curpos=0;
+ for (i=0;i<datalen;i++) {
+ if (datain[i]=='\n') {
+ /* almost empty lines must allocate CR+terminator */
+ zelines[flux_nblines]=MemMalloc(i-flux_curpos+2);
+ /* copy data+CR */
+ memcpy(zelines[flux_nblines],datain+flux_curpos,i-flux_curpos+1);
+ /* et on ajoute un petit zéro à la fin! */
+ zelines[flux_nblines][i-flux_curpos+1]=0;
+#if 0
+if (flux_nblines<50) printf("%02d[%s]\n",flux_nblines,zelines[flux_nblines]);
+#endif
+ flux_curpos=i+1;
+ flux_nblines++;
+ }
+ }
+ if (i>flux_curpos) {
+ zelines[flux_nblines]=MemMalloc(i-flux_curpos+1);
+ memcpy(zelines[flux_nblines],datain+flux_curpos,i-flux_curpos);
+ zelines[flux_nblines][i-flux_curpos]=0;
+ flux_nblines++;
+ }
+ /* terminator */
+ zelines[flux_nblines]=NULL;
+
+ /* en mode flux on prend le repertoire courant en reference */
+ FieldArrayAddDynamicValueConcat(&ae->filename,&ae->ifile,&ae->maxfile,CURRENT_DIR);
+ }
+
+#if TRACE_PREPRO
+printf("remove comz, do includes\n");
+#endif
+#if TRACE_GENERALE
+printf("-comz/include\n");
+#endif
+ EarlyPrepSrc(ae,zelines,ae->filename[ae->ifile-1]);
+
+ for (i=0;zelines[i];i++) {
+ curlisting.ifile=0;
+ curlisting.iline=i+1;
+ curlisting.listing=zelines[i];
+ ObjectArrayAddDynamicValueConcat((void**)&listing,&ilisting,&maxlisting,&curlisting,sizeof(curlisting));
+ }
+ MemFree(zelines);
+
+ /* on s'assure que la derniere instruction est prise en compte a peu de frais */
+ if (ilisting) {
+ datalen=strlen(listing[ilisting-1].listing);
+ listing[ilisting-1].listing=MemRealloc(listing[ilisting-1].listing,datalen+2);
+ listing[ilisting-1].listing[datalen]=':';
+ listing[ilisting-1].listing[datalen+1]=0;
+ }
+
+ waiting_quote=quote_type=0;
+ l=idx=0;
+ while (l<ilisting) {
+ c=listing[l].listing[idx++];
+ if (!c) {
+ l++;
+ idx=0;
+ continue;
+ } else if (c=='\\' && !waiting_quote) {
+ idx++;
+ continue;
+ } else if (c==0x0D || c==0x0A) {
+ listing[l].listing[idx-1]=':';
+ c=':';
+ } else if (c=='\'' && idx>2 && strncmp(&listing[l].listing[idx-3],"AF'",3)==0) {
+ /* rien */
+ } else if (c=='"' || c=='\'') {
+ if (!quote_type) {
+ quote_type=c;
+ lquote=l;
+ } else {
+ if (c==quote_type) {
+ quote_type=0;
+ }
+ }
+ }
+
+ if (waiting_quote) {
+ /* expecting quote and nothing else */
+ switch (waiting_quote) {
+ case 1:
+ if (c==quote_type) waiting_quote=2;
+ break;
+ case 2:
+ if (!quote_type) {
+ waiting_quote=3;
+ qval[iqval]=0;
+ } else {
+ qval[iqval++]=c;
+ StateMachineResizeBuffer(&qval,iqval,&sqval);
+ qval[iqval]=0;
+ }
+ }
+ if (waiting_quote==3) {
+ if (incbin) {
+ int fileok=0,ilookfile;
+ /* qval contient le nom du fichier a lire */
+ filename_toread=MergePath(ae,ae->filename[listing[l].ifile],qval);
+ if (FileExists(filename_toread)) {
+ fileok=1;
+ } else {
+ for (ilookfile=0;ilookfile<ae->ipath && !fileok;ilookfile++) {
+ filename_toread=MergePath(ae,ae->includepath[ilookfile],qval);
+ if (FileExists(filename_toread)) {
+ fileok=1;
+ }
+ }
+ }
+
+ curhexbin.filename=TxtStrDup(filename_toread);
+ curhexbin.crunch=crunch;
+ if (fileok) {
+ /* lecture */
+ curhexbin.rawlen=curhexbin.datalen=FileGetSize(filename_toread);
+ curhexbin.data=MemMalloc(curhexbin.datalen*1.3+10);
+ #if TRACE_PREPRO
+ switch (crunch) {
+ case 0:rasm_printf(ae,KBLUE"incbin [%s] size=%d\n",filename_toread,curhexbin.datalen);break;
+ case 4:rasm_printf(ae,KBLUE"inclz4 [%s] size=%d\n",filename_toread,curhexbin.datalen);break;
+ case 7:rasm_printf(ae,KBLUE"incsx7 [%s] size=%d\n",filename_toread,curhexbin.datalen);break;
+ case 8:rasm_printf(ae,KBLUE"incexo [%s] size=%d\n",filename_toread,curhexbin.datalen);break;
+ case 88:rasm_printf(ae,KBLUE"incexb [%s] size=%d\n",filename_toread,curhexbin.datalen);break;
+ case 48:rasm_printf(ae,KBLUE"incl48 [%s] size=%d\n",filename_toread,curhexbin.datalen);break;
+ case 49:rasm_printf(ae,KBLUE"incl49 [%s] size=%d\n",filename_toread,curhexbin.datalen);break;
+ default:rasm_printf(ae,KBLUE"invalid crunch state!\n");exit(-42);
+ }
+ #endif
+ if (FileReadBinary(filename_toread,(char*)curhexbin.data,curhexbin.datalen)!=curhexbin.datalen) {
+ rasm_printf(ae,"read error on %s",filename_toread);
+ exit(2);
+ }
+ FileReadBinaryClose(filename_toread);
+ switch (crunch) {
+ #ifndef NO_3RD_PARTIES
+ case 4:
+ newdata=LZ4_crunch(curhexbin.data,curhexbin.datalen,&curhexbin.datalen);
+ MemFree(curhexbin.data);
+ curhexbin.data=newdata;
+ #if TRACE_PREPRO
+ rasm_printf(ae,KVERBOSE"crunched with LZ4 into %d byte(s)\n",curhexbin.datalen);
+ #endif
+ break;
+ case 7:
+ {
+ size_t slzlen;
+ newdata=ZX7_compress(optimize(curhexbin.data, curhexbin.datalen), curhexbin.data, curhexbin.datalen, &slzlen);
+ curhexbin.datalen=slzlen;
+ MemFree(curhexbin.data);
+ curhexbin.data=newdata;
+ #if TRACE_PREPRO
+ rasm_printf(ae,KVERBOSE"crunched with ZX7 into %d byte(s)\n",curhexbin.datalen);
+ #endif
+ }
+ break;
+ case 8:
+ rasm_printf(ae,KWARNING"Exomizer is crunching %.1fkb this may take a while, be patient...\n",curhexbin.datalen/1024.0);
+ newdata=Exomizer_crunch(curhexbin.data,curhexbin.datalen,&curhexbin.datalen);
+ MemFree(curhexbin.data);
+ curhexbin.data=newdata;
+ #if TRACE_PREPRO
+ rasm_printf(ae,KVERBOSE"crunched with Exomizer into %d byte(s)\n",curhexbin.datalen);
+ #endif
+ break;
+ #endif
+ case 48:
+ newdata=LZ48_crunch(curhexbin.data,curhexbin.datalen,&curhexbin.datalen);
+ MemFree(curhexbin.data);
+ curhexbin.data=newdata;
+ #if TRACE_PREPRO
+ rasm_printf(ae,KVERBOSE"crunched with LZ48 into %d byte(s)\n",curhexbin.datalen);
+ #endif
+ break;
+ case 49:
+ newdata=LZ49_crunch(curhexbin.data,curhexbin.datalen,&curhexbin.datalen);
+ MemFree(curhexbin.data);
+ curhexbin.data=newdata;
+ #if TRACE_PREPRO
+ rasm_printf(ae,KVERBOSE"crunched into with LZ49 %d byte(s)\n",curhexbin.datalen);
+ #endif
+ break;
+ default:break;
+ }
+ } else {
+ /* TAG + info */
+ curhexbin.datalen=-1;
+ curhexbin.data=MemMalloc(2);
+ /* not yet an error, we will know later when executing the code */
+ }
+ ObjectArrayAddDynamicValueConcat((void**)&ae->hexbin,&ae->ih,&ae->mh,&curhexbin,sizeof(curhexbin));
+ /* insertion */
+ le=strlen(listing[l].listing);
+
+ newlistingline=MemMalloc(le+32);
+ memcpy(newlistingline,listing[l].listing,rewrite);
+ rewrite+=sprintf(newlistingline+rewrite,"HEXBIN #%X",ae->ih-1);
+ strcat(newlistingline+rewrite,listing[l].listing+idx);
+ idx=rewrite;
+ MemFree(listing[l].listing);
+ listing[l].listing=newlistingline;
+ incbin=0;
+ } else if (include) {
+ /* qval contient le nom du fichier a lire */
+ int fileok=0,ilookfile;
+ /* qval contient le nom du fichier a lire */
+ filename_toread=MergePath(ae,ae->filename[listing[l].ifile],qval);
+ if (FileExists(filename_toread)) {
+ fileok=1;
+ } else {
+ for (ilookfile=0;ilookfile<ae->ipath && !fileok;ilookfile++) {
+ filename_toread=MergePath(ae,ae->includepath[ilookfile],qval);
+ if (FileExists(filename_toread)) {
+ fileok=1;
+ }
+ }
+ }
+
+ if (fileok) {
+ int newi,newj;
+ #if TRACE_PREPRO
+ rasm_printf(ae,KBLUE"include [%s]\n",filename_toread);
+ #endif
+
+ /* lecture */
+ listing_include=FileReadLines(filename_toread);
+ FieldArrayAddDynamicValueConcat(&ae->filename,&ae->ifile,&ae->maxfile,filename_toread);
+ /* virer les commentaires + pré-traitement */
+ EarlyPrepSrc(ae,listing_include,ae->filename[ae->ifile-1]);
+
+ /* split de la ligne en cours + suppression de l'instruction include */
+ PreProcessingSplitListing(&listing,&ilisting,&maxlisting,l,rewrite,idx);
+ /* insertion des nouvelles lignes + reference fichier + numeros de ligne */
+ PreProcessingInsertListing(&listing,&ilisting,&maxlisting,l,listing_include,ae->ifile-1);
+
+ MemFree(listing_include); /* free le tableau mais pas les lignes */
+ listing_include=NULL;
+ idx=0; /* on reste sur la meme ligne mais on se prepare a relire du caractere 0! */
+ } else {
+ /* TAG + info */
+ curhexbin.filename=TxtStrDup(filename_toread);
+ curhexbin.datalen=-2;
+ curhexbin.data=MemMalloc(2);
+ /* not yet an error, we will know later when executing the code */
+ ObjectArrayAddDynamicValueConcat((void**)&ae->hexbin,&ae->ih,&ae->mh,&curhexbin,sizeof(curhexbin));
+ /* insertion */
+ le=strlen(listing[l].listing);
+ newlistingline=MemMalloc(le+32);
+ memcpy(newlistingline,listing[l].listing,rewrite);
+ rewrite+=sprintf(newlistingline+rewrite,"HEXBIN #%X",ae->ih-1);
+ strcat(newlistingline+rewrite,listing[l].listing+idx);
+ idx=rewrite;
+ MemFree(listing[l].listing);
+ listing[l].listing=newlistingline;
+ }
+ include=0;
+ }
+ waiting_quote=0;
+ qval[0]=0;
+ iqval=0;
+ }
+ } else {
+ /* classic behaviour */
+
+ /* looking for include/incbin */
+ if (((c>='A' && c<='Z') || (c>='0' && c<='9') || c=='@' || c=='_')&& !quote_type) {
+ bval[ival++]=c;
+ StateMachineResizeBuffer(&bval,ival,&sval);
+ bval[ival]=0;
+ } else {
+ if (strcmp(bval,"INCLUDE")==0) {
+ include=1;
+ waiting_quote=1;
+ rewrite=idx-7-1;
+ /* quote right after keyword */
+ if (c==quote_type) {
+ waiting_quote=2;
+ }
+ } else if (strcmp(bval,"READ")==0) {
+ include=1;
+ waiting_quote=1;
+ rewrite=idx-4-1;
+ /* quote right after keyword */
+ if (c==quote_type) {
+ waiting_quote=2;
+ }
+ } else if (strcmp(bval,"INCLZ4")==0) {
+ incbin=1;
+ crunch=4;
+ waiting_quote=1;
+ rewrite=idx-6-1;
+ /* quote right after keyword */
+ if (c==quote_type) {
+ waiting_quote=2;
+ }
+ } else if (strcmp(bval,"INCEXB")==0) {
+ incbin=1;
+ crunch=88;
+ waiting_quote=1;
+ rewrite=idx-6-1;
+ /* quote right after keyword */
+ if (c==quote_type) {
+ waiting_quote=2;
+ }
+ } else if (strcmp(bval,"INCEXO")==0) {
+ incbin=1;
+ crunch=8;
+ waiting_quote=1;
+ rewrite=idx-6-1;
+ /* quote right after keyword */
+ if (c==quote_type) {
+ waiting_quote=2;
+ }
+ } else if (strcmp(bval,"INCZX7")==0) {
+ incbin=1;
+ crunch=7;
+ waiting_quote=1;
+ rewrite=idx-6-1;
+ /* quote right after keyword */
+ if (c==quote_type) {
+ waiting_quote=2;
+ }
+ } else if (strcmp(bval,"INCL48")==0) {
+ incbin=1;
+ crunch=48;
+ waiting_quote=1;
+ rewrite=idx-6-1;
+ /* quote right after keyword */
+ if (c==quote_type) {
+ waiting_quote=2;
+ }
+ } else if (strcmp(bval,"INCL49")==0) {
+ incbin=1;
+ crunch=49;
+ waiting_quote=1;
+ rewrite=idx-6-1;
+ /* quote right after keyword */
+ if (c==quote_type) {
+ waiting_quote=2;
+ }
+ } else if (strcmp(bval,"INCBIN")==0) {
+ incbin=1;
+ crunch=0;
+ waiting_quote=1;
+ rewrite=idx-6-1;
+ /* quote right after keyword */
+ if (c==quote_type) {
+ waiting_quote=2;
+ }
+ } else if (strcmp(bval,"INCWAV")==0) {
+ incbin=1;
+ crunch=0;
+ waiting_quote=1;
+ rewrite=idx-6-1;
+ /* quote right after keyword */
+ if (c==quote_type) {
+ waiting_quote=2;
+ }
+ } else if (strcmp(bval,"WHILE")==0) {
+ /* remplir la structure repeat_index */
+ windex.ol=listing[l].iline;
+ windex.oidx=idx;
+ windex.ifile=ae->ifile-1;
+ ObjectArrayAddDynamicValueConcat((void**)&TABwindex,&nwi,&mwi,&windex,sizeof(windex));
+ } else if (strcmp(bval,"REPEAT")==0) {
+ /* remplir la structure repeat_index */
+ rindex.ol=listing[l].iline;
+ rindex.oidx=idx;
+ rindex.ifile=ae->ifile-1;
+ ObjectArrayAddDynamicValueConcat((void**)&TABrindex,&nri,&mri,&rindex,sizeof(rindex));
+ } else if (strcmp(bval,"WEND")==0) {
+ /* retrouver la structure repeat_index correspondant a l'ouverture */
+ for (wi=nwi-1;wi>=0;wi--) {
+ if (TABwindex[wi].cl==-1) {
+ TABwindex[wi].cl=c;
+ TABwindex[wi].cidx=idx;
+ break;
+ }
+ }
+ if (wi==-1) {
+ MakeError(ae,ae->filename[listing[l].ifile],listing[l].iline,"WEND refers to unknown WHILE\n");
+ //exit(1);
+ }
+ } else if (strcmp(bval,"REND")==0 || strcmp(bval,"UNTIL")==0) {
+ /* retrouver la structure repeat_index correspondant a l'ouverture */
+ for (ri=nri-1;ri>=0;ri--) {
+ if (TABrindex[ri].cl==-1) {
+ TABrindex[ri].cl=c;
+ TABrindex[ri].cidx=idx;
+ break;
+ }
+ }
+ if (ri==-1) {
+ MakeError(ae,ae->filename[listing[l].ifile],listing[l].iline,"%s refers to unknown REPEAT\n",bval);
+ //exit(1);
+ }
+
+ }
+ bval[0]=0;
+ ival=0;
+ }
+ }
+ }
+#if TRACE_PREPRO
+printf("check quotes and repeats\n");
+#endif
+ if (quote_type) {
+ MakeError(ae,ae->filename[listing[lquote].ifile],listing[lquote].iline,"quote opened was not closed\n");
+ //exit(1);
+ }
+
+ /* repeat expansion check */
+ for (ri=0;ri<nri;ri++) {
+ if (TABrindex[ri].cl==-1) {
+ MakeError(ae,ae->filename[TABrindex[ri].ifile],TABrindex[ri].ol,"REPEAT was not closed\n");
+ }
+ }
+
+ /* creer une liste de mots */
+ curw.w=TxtStrDup("BEGIN");
+ curw.l=0;
+ curw.ifile=0;
+ curw.t=1;
+ curw.e=0;
+ ObjectArrayAddDynamicValueConcat((void**)&wordlist,&nbword,&maxword,&curw,sizeof(curw));
+
+ /* pour les calculs d'adresses avec IX et IY on enregistre deux variables bidons du meme nom */
+ curw.e=2;
+ curw.w=TxtStrDup("IX~0");
+ ObjectArrayAddDynamicValueConcat((void**)&wordlist,&nbword,&maxword,&curw,sizeof(curw));
+ curw.w=TxtStrDup("IY~0");
+ ObjectArrayAddDynamicValueConcat((void**)&wordlist,&nbword,&maxword,&curw,sizeof(curw));
+ curw.e=0;
+
+#if TRACE_PREPRO
+ l=0;
+ while (l<ilisting) {
+ rasm_printf(ae,"listing[%d]\n%s\n",l,listing[l].listing);
+ l++;
+ }
+#endif
+#if TRACE_GENERALE
+printf("-build wordlist\n");
+#endif
+
+ texpr=quote_type=0;
+ l=lw=idx=0;
+ ispace=0;
+ w[0]=0;
+ while (l<ilisting) {
+ c=listing[l].listing[idx++];
+ if (!c) {
+ idx=0;
+ l++;
+ continue;
+ }
+
+ if (!quote_type) {
+#if TRACE_PREPRO
+//printf("c='%c' automate[c]=%d\n",c>31?c:'.',Automate[((int)c)&0xFF]);
+#endif
+ switch (Automate[((int)c)&0xFF]) {
+ case 0:
+ MakeError(ae,ae->filename[listing[l].ifile],listing[l].iline,"invalid char '%c' (%d) char %d\n",c,c,idx);
+#if TRACE_PREPRO
+printf("c='%c' automate[c]=%d\n",c>31?c:'.',Automate[((int)c)&0xFF]);
+#endif
+ exit(0);
+ break;
+ case 1:
+ if (c=='\'' && idx>2 && strncmp(&listing[l].listing[idx-3],"AF'",3)==0) {
+ w[lw++]=c;
+ StateMachineResizeBuffer(&w,lw,&mw);
+ w[lw]=0;
+ break;
+ } else if (c=='\'' || c=='"') {
+ quote_type=c;
+ /* debut d'une quote, on finalise le mot -> POURQUOI DONC? */
+ //idx--;
+#if TRACE_PREPRO
+printf("quote\n");
+#endif
+ /* on finalise le mot si on est en début d'une nouvelle instruction ET que c'est un SAVE */
+ if (strcmp(w,"SAVE")==0) {
+ idx--;
+ } else {
+ w[lw++]=c;
+ StateMachineResizeBuffer(&w,lw,&mw);
+ w[lw]=0;
+ break;
+ }
+ } else {
+ if (c!=' ' && c!='\t') {
+ w[lw++]=c;
+ StateMachineResizeBuffer(&w,lw,&mw);
+ w[lw]=0;
+ } else {
+ /* Winape/Maxam operator compatibility on expressions */
+#if TRACE_PREPRO
+printf("1/2 winape maxam operator test for [%s]\n",w+ispace);
+#endif
+ if (texpr) {
+ if (strcmp(w+ispace,"AND")==0) {
+ w[ispace]='&';
+ lw=ispace+1;
+ } else if (strcmp(w+ispace,"OR")==0) {
+#if TRACE_PREPRO
+printf("conversion OR vers |\n");
+#endif
+ w[ispace]='|';
+ lw=ispace+1;
+ } else if (strcmp(w+ispace,"MOD")==0) {
+ w[ispace]='m';
+ lw=ispace+1;
+ } else if (strcmp(w+ispace,"XOR")==0) {
+ w[ispace]='^';
+ lw=ispace+1;
+ } else if (strcmp(w+ispace,"%")==0) {
+ w[ispace]='m';
+ lw=ispace+1;
+ }
+ }
+ ispace=lw;
+ }
+ break;
+ }
+ case 2:
+ /* separator (space, tab, comma) */
+#if TRACE_PREPRO
+printf("*** separator='%c'\n",c);
+#endif
+
+ /* patch argument suit une expression d'évaluation (ASSERT) */
+ if (c==',') hadcomma=1;
+
+ if (lw) {
+ w[lw]=0;
+ if (texpr && !wordlist[nbword-1].t && wordlist[nbword-1].e && !hadcomma) {
+ /* pour compatibilite winape avec AND,OR,XOR */
+#if TRACE_PREPRO
+printf("2/2 winape maxam operator test for expression [%s]\n",w+ispace);
+#endif
+ if (strcmp(w,"AND")==0) {
+ wtmp=TxtStrDup("&");
+ } else if (strcmp(w,"OR")==0) {
+ wtmp=TxtStrDup("|");
+ } else if (strcmp(w,"XOR")==0) {
+ wtmp=TxtStrDup("^");
+ } else if (strcmp(w,"%")==0) {
+ wtmp=TxtStrDup("m");
+ } else {
+ wtmp=TxtStrDup(w);
+ }
+ /* on concatène le nouveau mot à l'expression */
+ nbword--;
+ lw=0;
+ for (li=0;wordlist[nbword].w[li];li++) {
+ w[lw++]=wordlist[nbword].w[li];
+ StateMachineResizeBuffer(&w,lw,&mw);
+ }
+ w[lw]=0;
+ MemFree(wordlist[nbword].w);
+
+ for (li=0;wtmp[li];li++) {
+ w[lw++]=wtmp[li];
+ StateMachineResizeBuffer(&w,lw,&mw);
+ }
+ w[lw]=0;
+ MemFree(wtmp);
+ /* et on modifie l'automate pour la suite! */
+ Automate[' ']=1;
+ Automate['\t']=1;
+ ispace=lw;
+ } else if (strcmp(w,"EQU")==0) {
+ /* il y avait un mot avant alors on va reorganiser la ligne */
+ nbword--;
+ lw=0;
+ for (li=0;wordlist[nbword].w[li];li++) {
+ w[lw++]=wordlist[nbword].w[li];
+ StateMachineResizeBuffer(&w,lw,&mw);
+ }
+ MemFree(wordlist[nbword].w);
+ curw.e=lw+1;
+ /* on ajoute l'egalite d'alias*/
+ w[lw++]='~';
+ StateMachineResizeBuffer(&w,lw,&mw);
+ w[lw]=0;
+ Automate[' ']=1;
+ Automate['\t']=1;
+ ispace=lw;
+ texpr=1;
+ } else {
+ curw.w=TxtStrDup(w);
+ curw.l=listing[l].iline;
+ curw.ifile=listing[l].ifile;
+ curw.t=0;
+#if TRACE_PREPRO
+if (curw.w[0]=='=') {
+ printf("(1) bug prout\n");
+ exit(1);
+}
+printf("ajout du mot [%s]\n",curw.w);
+#endif
+ ObjectArrayAddDynamicValueConcat((void**)&wordlist,&nbword,&maxword,&curw,sizeof(curw));
+ //texpr=0; /* reset expr */
+ curw.e=0;
+ lw=0;
+ w[lw]=0;
+
+ /* match keyword? then next spaces will be ignored*/
+ if (macro_trigger) {
+ struct s_macro_fast curmacrofast;
+ Automate[' ']=1;
+ Automate['\t']=1;
+ ispace=0;
+ texpr=1;
+#if TRACE_PREPRO
+printf("macro trigger w=[%s]\n",curw.w);
+#endif
+ /* add macro name to instruction pool for preprocessor but not struct or write */
+ if (macro_trigger=='M') {
+ curmacrofast.mnemo=curw.w;
+ curmacrofast.crc=GetCRC(curw.w);
+ ObjectArrayAddDynamicValueConcat((void**)&MacroFast,&idxmacrofast,&maxmacrofast,&curmacrofast,sizeof(struct s_macro_fast));
+ }
+ macro_trigger=0;
+ } else {
+ int keymatched=0;
+ if ((ifast=ae->fastmatch[(int)curw.w[0]])!=-1) {
+ while (instruction[ifast].mnemo[0]==curw.w[0]) {
+ if (strcmp(instruction[ifast].mnemo,curw.w)==0) {
+ keymatched=1;
+ if (strcmp(curw.w,"MACRO")==0 || strcmp(curw.w,"STRUCT")==0 || strcmp(curw.w,"WRITE")==0) {
+/* @@TODO AS80 compatibility patch!!! */
+ macro_trigger=curw.w[0];
+ } else {
+ Automate[' ']=1;
+ Automate['\t']=1;
+ ispace=0;
+ /* instruction en cours, le reste est a interpreter comme une expression */
+#if TRACE_PREPRO
+printf("instruction en cours\n");
+#endif
+ texpr=1;
+ }
+ break;
+ }
+ ifast++;
+ }
+ }
+ if (!keymatched) {
+ int macrocrc;
+ macrocrc=GetCRC(curw.w);
+ for (keymatched=0;keymatched<idxmacrofast;keymatched++) {
+ if (MacroFast[keymatched].crc==macrocrc)
+ if (strcmp(MacroFast[keymatched].mnemo,curw.w)==0) {
+ Automate[' ']=1;
+ Automate['\t']=1;
+ ispace=0;
+ /* macro en cours, le reste est a interpreter comme une expression */
+ texpr=1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ if (hadcomma) {
+ MakeError(ae,ae->filename[listing[l].ifile],listing[l].iline,"empty parameter\n");
+ }
+ }
+ break;
+ case 3:
+ /* fin de ligne, on remet l'automate comme il faut */
+#if TRACE_PREPRO
+printf("EOL\n");
+#endif
+ macro_trigger=0;
+ Automate[' ']=2;
+ Automate['\t']=2;
+ ispace=0;
+ texpr=0;
+ /* si le mot lu a plus d'un caractère */
+ if (lw) {
+ if (!wordlist[nbword-1].t && (wordlist[nbword-1].e || w[0]=='=') && !hadcomma) {
+ /* cas particulier d'ecriture libre */
+ /* bugfix inhibition 19.06.2018 */
+ /* ajout du terminateur? */
+ w[lw]=0;
+#if TRACE_PREPRO
+printf("nbword=%d w=[%s] ->",nbword,w);fflush(stdout);
+#endif
+ nbword--;
+ wordlist[nbword].w=MemRealloc(wordlist[nbword].w,strlen(wordlist[nbword].w)+lw+1);
+ strcat(wordlist[nbword].w,w);
+#if TRACE_PREPRO
+printf("%s\n",wordlist[nbword].w);
+#endif
+ /* on change de type! */
+ wordlist[nbword].t=1;
+ //ObjectArrayAddDynamicValueConcat((void**)&wordlist,&nbword,&maxword,&curw,sizeof(curw));
+ curw.e=0;
+ lw=0;
+ w[lw]=0;
+ } else if (nbword && strcmp(w,"EQU")==0) {
+ /* il y avait un mot avant alors on va reorganiser la ligne */
+ nbword--;
+ lw=0;
+ for (li=0;wordlist[nbword].w[li];li++) {
+ w[lw++]=wordlist[nbword].w[li];
+ StateMachineResizeBuffer(&w,lw,&mw);
+ w[lw]=0;
+ }
+ MemFree(wordlist[nbword].w);
+ /* on ajoute l'egalite ou comparaison! */
+ curw.e=lw+1;
+ w[lw++]='=';
+ StateMachineResizeBuffer(&w,lw,&mw);
+ w[lw]=0;
+ Automate[' ']=1;
+ Automate['\t']=1;
+ } else {
+ /* mot de fin de ligne, à priori pas une expression */
+ curw.w=TxtStrDup(w);
+ curw.l=listing[l].iline;
+ curw.ifile=listing[l].ifile;
+ curw.t=1;
+#if TRACE_PREPRO
+printf("mot de fin de ligne = [%s]\n",curw.w);
+if (curw.w[0]=='=') {
+ printf("(3) bug prout\n");
+ exit(1);
+}
+#endif
+ ObjectArrayAddDynamicValueConcat((void**)&wordlist,&nbword,&maxword,&curw,sizeof(curw));
+ curw.e=0;
+ lw=0;
+ w[lw]=0;
+ hadcomma=0;
+ }
+ } else {
+ /* sinon c'est le précédent qui était terminateur d'instruction */
+ wordlist[nbword-1].t=1;
+ w[lw]=0;
+ }
+ hadcomma=0;
+ break;
+ case 4:
+#if TRACE_PREPRO
+printf("expr operator=%c\n",c);
+#endif
+ /* expression/condition */
+ texpr=1;
+ if (lw) {
+ Automate[' ']=1;
+ Automate['\t']=1;
+ if (!curw.e) {
+ curw.e=lw+1;
+ w[lw++]=c;
+ StateMachineResizeBuffer(&w,lw,&mw);
+ w[lw]=0;
+ } else {
+ w[lw++]=c;
+ StateMachineResizeBuffer(&w,lw,&mw);
+ w[lw]=0;
+ }
+ } else {
+ /* 2018.06.06 évolution sur le ! (not) */
+#if TRACE_PREPRO
+printf("*** operateur commence le mot\n");
+printf("mot precedent=[%s] t=%d\n",wordlist[nbword-1].w,wordlist[nbword-1].t);
+#endif
+ if (hadcomma && c=='!') {
+ /* on peut commencer un argument par un NOT */
+ w[lw++]=c;
+ StateMachineResizeBuffer(&w,lw,&mw);
+ w[lw]=0;
+ /* automate déjà modifié rien de plus */
+ } else if (!wordlist[nbword-1].t) {
+ /* il y avait un mot avant alors on va reorganiser la ligne */
+ /* patch NOT -> SAUF si c'est une directive */
+ int keymatched=0;
+ if ((ifast=ae->fastmatch[(int)wordlist[nbword-1].w[0]])!=-1) {
+ while (instruction[ifast].mnemo[0]==wordlist[nbword-1].w[0]) {
+ if (strcmp(instruction[ifast].mnemo,wordlist[nbword-1].w)==0) {
+ keymatched=1;
+ break;
+ }
+ ifast++;
+ }
+ }
+ if (!keymatched) {
+ int macrocrc;
+ macrocrc=GetCRC(wordlist[nbword-1].w);
+ for (i=0;i<idxmacrofast;i++) {
+ if (MacroFast[i].crc==macrocrc)
+ if (strcmp(MacroFast[i].mnemo,wordlist[nbword-1].w)==0) {
+ keymatched=1;
+ break;
+ }
+ }
+ }
+ if (!keymatched) {
+ nbword--;
+ for (li=0;wordlist[nbword].w[li];li++) {
+ w[lw++]=wordlist[nbword].w[li];
+ StateMachineResizeBuffer(&w,lw,&mw);
+ w[lw]=0;
+ }
+ MemFree(wordlist[nbword].w);
+ /* on ajoute l'egalite ou comparaison! */
+ curw.e=lw+1;
+ }
+ w[lw++]=c;
+ StateMachineResizeBuffer(&w,lw,&mw);
+ w[lw]=0;
+ /* et on modifie l'automate pour la suite! */
+ Automate[' ']=1;
+ Automate['\t']=1;
+ } else {
+ MakeError(ae,ae->filename[listing[l].ifile],listing[l].iline,"cannot start expression with '=','!','<','>'\n");
+ }
+ }
+ break;
+ default:
+ rasm_printf(ae,KERROR"Internal error (Automate wrong value=%d)\n",Automate[c]);
+ exit(-1);
+ }
+ } else {
+ /* lecture inconditionnelle de la quote */
+#if TRACE_PREPRO
+printf("quote[%d]=%c\n",lw,c);
+#endif
+ w[lw++]=c;
+ StateMachineResizeBuffer(&w,lw,&mw);
+ w[lw]=0;
+ if (!escape_code) {
+ if (c=='\\') escape_code=1;
+ if (lw>1 && c==quote_type) {
+ quote_type=0;
+ }
+ } else {
+ escape_code=0;
+ }
+ }
+ }
+
+#if TRACE_PREPRO
+printf("END\n");
+#endif
+
+ curw.w="END";
+ curw.l=0;
+ curw.t=2;
+ curw.ifile=0;
+ ObjectArrayAddDynamicValueConcat((void**)&wordlist,&nbword,&maxword,&curw,sizeof(curw));
+#if TRACE_PREPRO
+ rasm_printf(ae,KVERBOSE"wordlist contains %d element%s\n",nbword,nbword>1?"s":"");
+#endif
+ ae->nbword=nbword;
+
+ /* switch words for macro declaration with AS80 & UZ80 */
+ if (param && param->as80) {
+ for (l=0;l<nbword;l++) {
+ if (!wordlist[l].t && !wordlist[l].e && strcmp(wordlist[l+1].w,"MACRO")==0) {
+ char *wtmp;
+ wtmp=wordlist[l+1].w;
+ wordlist[l+1].w=wordlist[l].w;
+ wordlist[l].w=wtmp;
+ }
+ }
+ }
+
+#if TRACE_PREPRO
+ for (l=0;l<nbword;l++) {
+ rasm_printf(ae,"[%s]e=%d ",wordlist[l].w,wordlist[l].e);
+ if (wordlist[l].t) rasm_printf(ae,"(%d)\n",wordlist[l].t);
+ }
+
+printf("free\n");
+#endif
+
+ MemFree(bval);
+ MemFree(qval);
+ MemFree(w);
+
+ for (l=0;l<ilisting;l++) {
+ MemFree(listing[l].listing);
+ }
+ MemFree(listing);
+ /* wordlist
+ type 0: label or instruction followed by parameter(s)
+ type 1: last word of the line, last parameter of an instruction
+ type 2: very last word of the list
+ e tag: non zero if there is comparison or equality
+ */
+ ae->wl=wordlist;
+ if (param) {
+ MemFree(param->filename);
+ }
+ if (MacroFast) MemFree(MacroFast);
+ if (TABwindex) MemFree(TABwindex);
+ if (TABrindex) MemFree(TABrindex);
+#if TRACE_PREPRO
+printf("return ae\n");
+#endif
+ return ae;
+}
+
+int Rasm(struct s_parameter *param)
+{
+ #undef FUNC
+ #define FUNC "Rasm"
+
+ struct s_assenv *ae=NULL;
+
+ /* read and preprocess source */
+ ae=PreProcessing(param->filename,0,NULL,0,param);
+ /* assemble */
+ return Assemble(ae,NULL,NULL,NULL);
+}
+
+/* fonction d'export */
+
+int RasmAssemble(const char *datain, int lenin, unsigned char **dataout, int *lenout)
+{
+ struct s_assenv *ae=NULL;
+
+ if (lenout) *lenout=0;
+ ae=PreProcessing(NULL,1,datain,lenin,NULL);
+ return Assemble(ae,dataout,lenout,NULL);
+}
+
+int RasmAssembleInfo(const char *datain, int lenin, unsigned char **dataout, int *lenout, struct s_rasm_info **debug)
+{
+ struct s_assenv *ae=NULL;
+ int ret;
+
+ ae=PreProcessing(NULL,1,datain,lenin,NULL);
+ ret=Assemble(ae,dataout,lenout,debug);
+ return ret;
+}
+
+
+#define AUTOTEST_PAGELABELGEN "buildsna: bank: cpt=5: ld bc,{page}miam{cpt}: bank cpt: nop: miam{cpt} nop: assert {page}miam{cpt}==0x7FC5 "
+
+#define AUTOTEST_NOINCLUDE "truc equ 0:if truc:include'bite':endif:nop"
+
+#define AUTOTEST_SETINSIDE "ld hl,0=0xC9FB"
+
+#define AUTOTEST_OPERATOR_CONVERSION "ld hl,10 OR 20:ld a,40 and 10:ld bc,5 MOD 2:ld a,(ix+45 xor 45)"
+
+#define AUTOTEST_OPERATOR_MODULO "revar=46: devar=5 : var=46%5 : assert var==1: var=46 % 5 : assert var==1: var=46 mod 5 : assert var==1:" \
+ "var=revar%5 : assert var==1: var=revar%devar : assert var==1: var=46%devar : assert var==1: var=revar % 5 : assert var==1:" \
+ "var=revar % devar : assert var==1: var=46 % devar : assert var==1: var=revar % %101 : assert var==1: var=revar%%101 : assert var==1: nop"
+
+#define AUTOTEST_UNDEF "mavar=10: ifdef mavar: undef mavar: endif: ifdef mavar: fail 'undef did not work': endif:nop "
+
+#define AUTOTEST_INSTRMUSTFAILED "ld a,b,c:ldi a: ldir bc:exx hl,de:exx de:ex bc,hl:ex hl,bc:ex af,af:ex hl,hl:ex hl:exx hl: "\
+ "neg b:push b:push:pop:pop c:sub ix:add ix:add:sub:di 2:ei 3:ld i,c:ld r,e:rl:rr:rlca a:sla:sll:"\
+ "ldd e:lddr hl:adc ix:adc b,a:xor 12,13:xor b,1:xor:or 12,13:or b,1:or:and 12,13:and b,1:and:inc:dec"
+
+#define AUTOTEST_VIRGULE "defb 5,5,,5"
+#define AUTOTEST_VIRGULE2 "print '5,,5':nop"
+
+#define AUTOTEST_OVERLOADMACPRM "macro test,idx: defb idx:endm:macro test2,idx:defb {idx}:endm:repeat 2,idx:test idx-1:test2 idx-1:rend"
+
+#define AUTOTEST_IFDEFMACRO "macro test:nop:endm:ifndef test:error:else:test:endif:ifdef test:test:else:error:endif:nop"
+
+#define AUTOTEST_PRINTVAR "label1: macro test, param: print 'param {param}', {hex}{param}: endm:: test label1: nop"
+
+#define AUTOTEST_PRINTSPACE "idx=5: print 'grouik { idx + 3 } ':nop"
+
+#define AUTOTEST_NOT "myvar=10:myvar=10+myvar:if 5!=3:else:print glop:endif:ifnot 5:print glop:else:endif:" \
+ "ifnot 0:else:print glop:endif:if !(5):print glop:endif:if !(0):else:print glop:endif:" \
+ "ya=!0:if ya==1:else:print glop:endif:if !5:print glop:endif:ya = 0:ya =! 0:if ya == 1:" \
+ "else:print glop:endif:if ! 5:print glop:endif:if 1-!( !0 && !0):else:print glop:endif:nop"
+
+
+#define AUTOTEST_MACRO "macro glop:@glop:ld hl,@next:djnz @glop:@next:mend:macro glop2:@glop:glop:ld hl,@next:djnz @glop:glop:" \
+ "@next:mend:cpti=0:repeat:glop:cpt=0:glop:repeat:glop2:repeat 1:@glop:dec a:ld hl,@next:glop2:glop2:" \
+ "jr nz,@glop:@next:rend:cpt=cpt+1:glop2:until cpt<3:cpti=cpti+1:glop2:until cpti<3"
+
+#define AUTOTEST_MACRO_ADV "idx=10:macro mac2 param1,param2:ld hl,{param1}{idx+10}{param2}:{param1}{idx+10}{param2}:djnz {param1}{idx+10}{param2}:mend: " \
+ "mac2 label,45:mac2 glop,10:djnz glop2010:jp label2045"
+
+#define AUTOTEST_MACROPAR "macro unemac, param1, param2:defb '{param1}':defb {param2}:mend:unemac grouik,'grouik'"
+
+#define AUTOTEST_OPCODES "nop::ld bc,#1234::ld (bc),a::inc bc:inc b:dec b:ld b,#12:rlca:ex af,af':add hl,bc:ld a,(bc):dec bc:" \
+ "inc c:dec c:ld c,#12:rrca::djnz $:ld de,#1234:ld (de),a:inc de:inc d:dec d:ld d,#12:rla:jr $:" \
+ "add hl,de:ld a,(de):dec de:inc e:dec e:ld e,#12:rra::jr nz,$:ld hl,#1234:ld (#1234),hl:inc hl:inc h:" \
+ "dec h:ld h,#12:daa:jr z,$:add hl,hl:ld hl,(#1234):dec hl:inc l:dec l:ld l,#12:cpl::jr nc,$:" \
+ "ld sp,#1234:ld (#1234),a:inc sp:inc (hl):dec (hl):ld (hl),#12:scf:jr c,$:add hl,sp:ld a,(#1234):" \
+ "dec sp:inc a:dec a:ld a,#12:ccf::ld b,b:ld b,c:ld b,d:ld b,e:ld b,h:ld b,l:ld b,(hl):ld b,a:ld c,b:" \
+ "ld c,c:ld c,d:ld c,e:ld c,h:ld c,l:ld c,(hl):ld c,a::ld d,b:ld d,c:ld d,d:ld d,e:ld d,h:ld d,l:" \
+ "ld d,(hl):ld d,a:ld e,b:ld e,c:ld e,d:ld e,e:ld e,h:ld e,l:ld e,(hl):ld e,a::ld h,b:ld h,c:ld h,d:" \
+ "ld h,e:ld h,h:ld h,l:ld h,(hl):ld h,a:ld l,b:ld l,c:ld l,d:ld l,e:ld l,h:ld l,l:ld l,(hl):ld l,a::" \
+ "ld (hl),b:ld (hl),c:ld (hl),d:ld (hl),e:ld (hl),h:ld (hl),l:halt:ld (hl),a:ld a,b:ld a,c:ld a,d:" \
+ "ld a,e:ld a,h:ld a,l:ld a,(hl):ld a,a::add b:add c:add d:add e:add h:add l:add (hl):add a:adc b:" \
+ "adc c:adc d:adc e:adc h:adc l:adc (hl):adc a::sub b:sub c:sub d:sub e:sub h:sub l:sub (hl):sub a:" \
+ "sbc b:sbc c:sbc d:sbc e:sbc h:sbc l:sbc (hl):sbc a::and b:and c:and d:and e:and h:and l:and (hl):" \
+ "and a:xor b:xor c:xor d:xor e:xor h:xor l:xor (hl):xor a::or b:or c:or d:or e:or h:or l:or (hl):" \
+ "or a:cp b:cp c:cp d:cp e:cp h:cp l:cp (hl):cp a::ret nz:pop bc:jp nz,#1234:jp #1234:call nz,#1234:" \
+ "push bc:add #12:rst 0:ret z:ret:jp z,#1234:nop:call z,#1234:call #1234:adc #12:rst 8::ret nc:pop de:" \
+ "jp nc,#1234:out (#12),a:call nc,#1234:push de:sub #12:rst #10:ret c:exx:jp c,#1234:in a,(#12):" \
+ "call c,#1234:nop:sbc #12:rst #18::ret po:pop hl:jp po,#1234:ex (sp),hl:call po,#1234:push hl:" \
+ "and #12:rst #20:ret pe:jp (hl):jp pe,#1234:ex de,hl:call pe,#1234:nop:xor #12:rst #28::ret p:pop af:" \
+ "jp p,#1234:di:call p,#1234:push af:or #12:rst #30:ret m:ld sp,hl:jp m,#1234:ei:call m,#1234:nop:" \
+ "cp #12:rst #38:in b,(c):out (c),b:sbc hl,bc:ld (#1234),bc:neg:retn:im 0:ld i,a:in c,(c):out (c),c:" \
+ "adc hl,bc:ld bc,(#1234):reti:ld r,a::in d,(c):out (c),d:sbc hl,de:ld (#1234),de:retn:im 1:ld a,i:" \
+ "in e,(c):out (c),e:adc hl,de:ld de,(#1234):im 2:ld a,r::in h,(c):out (c),h:sbc hl,hl:rrd:in l,(c):" \
+ "out (c),l:adc hl,hl:rld::in 0,(c):out (c),0:sbc hl,sp:ld (#1234),sp:in a,(c):out (c),a:adc hl,sp:" \
+ "ld sp,(#1234)::ldi:cpi:ini:outi:ldd:cpd:ind:outd::ldir:cpir:inir:otir:lddr:cpdr:indr:otdr::rlc b:" \
+ "rlc c:rlc d:rlc e:rlc h:rlc l:rlc (hl):rlc a:rrc b:rrc c:rrc d:rrc e:rrc h:rrc l:rrc (hl):rrc a::" \
+ "rl b:rl c:rl d:rl e:rl h:rl l:rl (hl):rl a:rr b:rr c:rr d:rr e:rr h:rr l:rr (hl):rr a:sla b:sla c:" \
+ "sla d:sla e:sla h:sla l:sla (hl):sla a:sra b:sra c:sra d:sra e:sra h:sra l:sra (hl):sra a::sll b:" \
+ "sll c:sll d:sll e:sll h:sll l:sll (hl):sll a:srl b:srl c:srl d:srl e:srl h:srl l:srl (hl):srl a::" \
+ "bit 0,b:bit 0,c:bit 0,d:bit 0,e:bit 0,h:bit 0,l:bit 0,(hl):bit 0,a::bit 1,b:bit 1,c:bit 1,d:bit 1,e:" \
+ "bit 1,h:bit 1,l:bit 1,(hl):bit 1,a::bit 2,b:bit 2,c:bit 2,d:bit 2,e:bit 2,h:bit 2,l:bit 2,(hl):" \
+ "bit 2,a::bit 3,b:bit 3,c:bit 3,d:bit 3,e:bit 3,h:bit 3,l:bit 3,(hl):bit 3,a::bit 4,b:bit 4,c:" \
+ "bit 4,d:bit 4,e:bit 4,h:bit 4,l:bit 4,(hl):bit 4,a::bit 5,b:bit 5,c:bit 5,d:bit 5,e:bit 5,h:bit 5,l:" \
+ "bit 5,(hl):bit 5,a::bit 6,b:bit 6,c:bit 6,d:bit 6,e:bit 6,h:bit 6,l:bit 6,(hl):bit 6,a::bit 7,b:" \
+ "bit 7,c:bit 7,d:bit 7,e:bit 7,h:bit 7,l:bit 7,(hl):bit 7,a::res 0,b:res 0,c:res 0,d:res 0,e:res 0,h:" \
+ "res 0,l:res 0,(hl):res 0,a::res 1,b:res 1,c:res 1,d:res 1,e:res 1,h:res 1,l:res 1,(hl):res 1,a::" \
+ "res 2,b:res 2,c:res 2,d:res 2,e:res 2,h:res 2,l:res 2,(hl):res 2,a::res 3,b:res 3,c:res 3,d:res 3,e:" \
+ "res 3,h:res 3,l:res 3,(hl):res 3,a::res 4,b:res 4,c:res 4,d:res 4,e:res 4,h:res 4,l:res 4,(hl):" \
+ "res 4,a::res 5,b:res 5,c:res 5,d:res 5,e:res 5,h:res 5,l:res 5,(hl):res 5,a::res 6,b:res 6,c:" \
+ "res 6,d:res 6,e:res 6,h:res 6,l:res 6,(hl):res 6,a::res 7,b:res 7,c:res 7,d:res 7,e:res 7,h:res 7,l:" \
+ "res 7,(hl):res 7,a::set 0,b:set 0,c:set 0,d:set 0,e:set 0,h:set 0,l:set 0,(hl):set 0,a::set 1,b:" \
+ "set 1,c:set 1,d:set 1,e:set 1,h:set 1,l:set 1,(hl):set 1,a::set 2,b:set 2,c:set 2,d:set 2,e:set 2,h:" \
+ "set 2,l:set 2,(hl):set 2,a::set 3,b:set 3,c:set 3,d:set 3,e:set 3,h:set 3,l:set 3,(hl):set 3,a::" \
+ "set 4,b:set 4,c:set 4,d:set 4,e:set 4,h:set 4,l:set 4,(hl):set 4,a::set 5,b:set 5,c:set 5,d:set 5,e:" \
+ "set 5,h:set 5,l:set 5,(hl):set 5,a::set 6,b:set 6,c:set 6,d:set 6,e:set 6,h:set 6,l:set 6,(hl):" \
+ "set 6,a::set 7,b:set 7,c:set 7,d:set 7,e:set 7,h:set 7,l:set 7,(hl):set 7,a::add ix,bc::add ix,de::" \
+ "ld ix,#1234:ld (#1234),ix:inc ix:inc xh:dec xh:ld xh,#12:add ix,ix:ld ix,(#1234):dec ix:inc xl:" \
+ "dec xl:ld xl,#12::inc (ix+#12):dec (ix+#12):ld (ix+#12),#34:add ix,sp::ld b,xh:ld b,xl:" \
+ "ld b,(ix+#12):ld c,xh:ld c,xl:ld c,(ix+#12):::ld d,xh:ld d,xl:ld d,(ix+#12):ld e,xh:ld e,xl:" \
+ "ld e,(ix+#12)::ld xh,b:ld xh,c:ld xh,d:ld xh,e:ld xh,xh:ld xh,xl:ld h,(ix+#12):ld xh,a:ld xl,b:" \
+ "ld xl,c:ld xl,d:ld xl,e:ld xl,xh:ld xl,xl:ld l,(ix+#12):ld xl,a::ld (ix+#12),b:ld (ix+#12),c:" \
+ "ld (ix+#12),d:ld (ix+#12),e:ld (ix+#12),h:ld (ix+#12),l:ld (ix+#12),a:ld a,xh:ld a,xl:" \
+ "ld a,(ix+#12)::add xh:add xl:add (ix+#12):adc xh:adc xl:adc (ix+#12)::sub xh:sub xl:sub (ix+#12):" \
+ "sbc xh:sbc xl:sbc (ix+#12)::and xh:and xl:and (ix+#12):xor xh:xor xl:xor (ix+#12)::or xh:or xl:" \
+ "or (ix+#12):cp xh:cp xl:cp (ix+#12)::pop ix:ex (sp),ix:push ix:jp (ix)::ld sp,ix:::rlc (ix+#12),b:" \
+ "rlc (ix+#12),c:rlc (ix+#12),d:rlc (ix+#12),e:rlc (ix+#12),h:rlc (ix+#12),l:rlc (ix+#12):" \
+ "rlc (ix+#12),a:rrc (ix+#12),b:rrc (ix+#12),c:rrc (ix+#12),d:rrc (ix+#12),e:rrc (ix+#12),h:" \
+ "rrc (ix+#12),l:rrc (ix+#12):rrc (ix+#12),a::rl (ix+#12),b:rl (ix+#12),c:rl (ix+#12),d:rl (ix+#12),e:" \
+ "rl (ix+#12),h:rl (ix+#12),l:rl (ix+#12):rl (ix+#12),a:rr (ix+#12),b:rr (ix+#12),c:rr (ix+#12),d:" \
+ "rr (ix+#12),e:rr (ix+#12),h:rr (ix+#12),l:rr (ix+#12):rr (ix+#12),a::sla (ix+#12),b:sla (ix+#12),c:" \
+ "sla (ix+#12),d:sla (ix+#12),e:sla (ix+#12),h:sla (ix+#12),l:sla (ix+#12):sla (ix+#12),a:" \
+ "sra (ix+#12),b:sra (ix+#12),c:sra (ix+#12),d:sra (ix+#12),e:sra (ix+#12),h:sra (ix+#12),l:" \
+ "sra (ix+#12):sra (ix+#12),a::sll (ix+#12),b:sll (ix+#12),c:sll (ix+#12),d:sll (ix+#12),e:" \
+ "sll (ix+#12),h:sll (ix+#12),l:sll (ix+#12):sll (ix+#12),a:srl (ix+#12),b:srl (ix+#12),c:" \
+ "srl (ix+#12),d:srl (ix+#12),e:srl (ix+#12),h:srl (ix+#12),l:srl (ix+#12):srl (ix+#12),a::" \
+ "bit 0,(ix+#12):bit 1,(ix+#12):bit 2,(ix+#12):bit 3,(ix+#12):bit 4,(ix+#12):bit 5,(ix+#12):" \
+ "bit 6,(ix+#12):bit 7,(ix+#12):bit 0,(ix+#12),d:bit 1,(ix+#12),b:bit 2,(ix+#12),c:bit 3,(ix+#12),d:" \
+ "bit 4,(ix+#12),e:bit 5,(ix+#12),h:bit 6,(ix+#12),l:bit 7,(ix+#12),a:::res 0,(ix+#12),b:" \
+ "res 0,(ix+#12),c:res 0,(ix+#12),d:res 0,(ix+#12),e:res 0,(ix+#12),h:res 0,(ix+#12),l:res 0,(ix+#12):" \
+ "res 0,(ix+#12),a::res 1,(ix+#12),b:res 1,(ix+#12),c:res 1,(ix+#12),d:res 1,(ix+#12),e:" \
+ "res 1,(ix+#12),h:res 1,(ix+#12),l:res 1,(ix+#12):res 1,(ix+#12),a::res 2,(ix+#12),b:" \
+ "res 2,(ix+#12),c:res 2,(ix+#12),d:res 2,(ix+#12),e:res 2,(ix+#12),h:res 2,(ix+#12),l:res 2,(ix+#12):" \
+ "res 2,(ix+#12),a::res 3,(ix+#12),b:res 3,(ix+#12),c:res 3,(ix+#12),d:res 3,(ix+#12),e:" \
+ "res 3,(ix+#12),h:res 3,(ix+#12),l:res 3,(ix+#12):res 3,(ix+#12),a::res 4,(ix+#12),b:" \
+ "res 4,(ix+#12),c:res 4,(ix+#12),d:res 4,(ix+#12),e:res 4,(ix+#12),h:res 4,(ix+#12),l:" \
+ "res 4,(ix+#12):res 4,(ix+#12),a::res 5,(ix+#12),b:res 5,(ix+#12),c:res 5,(ix+#12),d:" \
+ "res 5,(ix+#12),e:res 5,(ix+#12),h:res 5,(ix+#12),l:res 5,(ix+#12):res 5,(ix+#12),a::" \
+ "res 6,(ix+#12),b:res 6,(ix+#12),c:res 6,(ix+#12),d:res 6,(ix+#12),e:res 6,(ix+#12),h:" \
+ "res 6,(ix+#12),l:res 6,(ix+#12):res 6,(ix+#12),a::res 7,(ix+#12),b:res 7,(ix+#12),c:" \
+ "res 7,(ix+#12),d:res 7,(ix+#12),e:res 7,(ix+#12),h:res 7,(ix+#12),l:res 7,(ix+#12):" \
+ "res 7,(ix+#12),a::set 0,(ix+#12),b:set 0,(ix+#12),c:set 0,(ix+#12),d:set 0,(ix+#12),e:" \
+ "set 0,(ix+#12),h:set 0,(ix+#12),l:set 0,(ix+#12):set 0,(ix+#12),a::set 1,(ix+#12),b:" \
+ "set 1,(ix+#12),c:set 1,(ix+#12),d:set 1,(ix+#12),e:set 1,(ix+#12),h:set 1,(ix+#12),l:" \
+ "set 1,(ix+#12):set 1,(ix+#12),a::set 2,(ix+#12),b:set 2,(ix+#12),c:set 2,(ix+#12),d:" \
+ "set 2,(ix+#12),e:set 2,(ix+#12),h:set 2,(ix+#12),l:set 2,(ix+#12):set 2,(ix+#12),a::" \
+ "set 3,(ix+#12),b:set 3,(ix+#12),c:set 3,(ix+#12),d:set 3,(ix+#12),e:set 3,(ix+#12),h:" \
+ "set 3,(ix+#12),l:set 3,(ix+#12):set 3,(ix+#12),a::set 4,(ix+#12),b:set 4,(ix+#12),c:" \
+ "set 4,(ix+#12),d:set 4,(ix+#12),e:set 4,(ix+#12),h:set 4,(ix+#12),l:set 4,(ix+#12):" \
+ "set 4,(ix+#12),a::set 5,(ix+#12),b:set 5,(ix+#12),c:set 5,(ix+#12),d:set 5,(ix+#12),e:" \
+ "set 5,(ix+#12),h:set 5,(ix+#12),l:set 5,(ix+#12):set 5,(ix+#12),a::set 6,(ix+#12),b:" \
+ "set 6,(ix+#12),c:set 6,(ix+#12),d:set 6,(ix+#12),e:set 6,(ix+#12),h:set 6,(ix+#12),l:" \
+ "set 6,(ix+#12):set 6,(ix+#12),a::set 7,(ix+#12),b:set 7,(ix+#12),c:set 7,(ix+#12),d:" \
+ "set 7,(ix+#12),e:set 7,(ix+#12),h:set 7,(ix+#12),l:set 7,(ix+#12):set 7,(ix+#12),a::add iy,bc::" \
+ "add iy,de::ld iy,#1234:ld (#1234),iy:inc iy:inc yh:dec yh:ld yh,#12:add iy,iy:ld iy,(#1234):dec iy:" \
+ "inc yl:dec yl:ld yl,#12::inc (iy+#12):dec (iy+#12):ld (iy+#12),#34:add iy,sp::ld b,yh:ld b,yl:" \
+ "ld b,(iy+#12):ld c,yh:ld c,yl:ld c,(iy+#12):::ld d,yh:ld d,yl:ld d,(iy+#12):ld e,yh:ld e,yl:" \
+ "ld e,(iy+#12)::ld yh,b:ld yh,c:ld yh,d:ld yh,e:ld yh,yh:ld yh,yl:ld h,(iy+#12):ld yh,a:ld yl,b:" \
+ "ld yl,c:ld yl,d:ld yl,e:ld yl,yh:ld yl,yl:ld l,(iy+#12):ld yl,a::ld (iy+#12),b:ld (iy+#12),c:" \
+ "ld (iy+#12),d:ld (iy+#12),e:ld (iy+#12),h:ld (iy+#12),l:ld (iy+#12),a:ld a,yh:ld a,yl:" \
+ "ld a,(iy+#12)::add yh:add yl:add (iy+#12):adc yh:adc yl:adc (iy+#12)::sub yh:sub yl:" \
+ "sub (iy+#12):sbc yh:sbc yl:sbc (iy+#12)::and yh:and yl:and (iy+#12):xor yh:xor yl:xor (iy+#12)::" \
+ "or yh:or yl:or (iy+#12):cp yh:cp yl:cp (iy+#12)::pop iy:ex (sp),iy:push iy:jp (iy)::ld sp,iy::" \
+ "rlc (iy+#12),b:rlc (iy+#12),c:rlc (iy+#12),d:rlc (iy+#12),e:rlc (iy+#12),h:rlc (iy+#12),l:" \
+ "rlc (iy+#12):rlc (iy+#12),a:rrc (iy+#12),b:rrc (iy+#12),c:rrc (iy+#12),d:rrc (iy+#12),e:" \
+ "rrc (iy+#12),h:rrc (iy+#12),l:rrc (iy+#12):rrc (iy+#12),a::rl (iy+#12),b:rl (iy+#12),c:" \
+ "rl (iy+#12),d:rl (iy+#12),e:rl (iy+#12),h:rl (iy+#12),l:rl (iy+#12):rl (iy+#12),a:rr (iy+#12),b:" \
+ "rr (iy+#12),c:rr (iy+#12),d:rr (iy+#12),e:rr (iy+#12),h:rr (iy+#12),l:rr (iy+#12):rr (iy+#12),a::" \
+ "sla (iy+#12),b:sla (iy+#12),c:sla (iy+#12),d:sla (iy+#12),e:sla (iy+#12),h:sla (iy+#12),l:" \
+ "sla (iy+#12):sla (iy+#12),a:sra (iy+#12),b:sra (iy+#12),c:sra (iy+#12),d:sra (iy+#12),e:" \
+ "sra (iy+#12),h:sra (iy+#12),l:sra (iy+#12):sra (iy+#12),a::sll (iy+#12),b:sll (iy+#12),c:" \
+ "sll (iy+#12),d:sll (iy+#12),e:sll (iy+#12),h:sll (iy+#12),l:sll (iy+#12):sll (iy+#12),a:" \
+ "srl (iy+#12),b:srl (iy+#12),c:srl (iy+#12),d:srl (iy+#12),e:srl (iy+#12),h:srl (iy+#12),l:" \
+ "srl (iy+#12):srl (iy+#12),a::bit 0,(iy+#12):bit 1,(iy+#12):bit 2,(iy+#12):bit 3,(iy+#12):" \
+ "bit 4,(iy+#12):bit 5,(iy+#12):bit 6,(iy+#12):bit 7,(iy+#12)::res 0,(iy+#12),b:res 0,(iy+#12),c:" \
+ "res 0,(iy+#12),d:res 0,(iy+#12),e:res 0,(iy+#12),h:res 0,(iy+#12),l:res 0,(iy+#12):" \
+ "res 0,(iy+#12),a::res 1,(iy+#12),b:res 1,(iy+#12),c:res 1,(iy+#12),d:res 1,(iy+#12),e:" \
+ "res 1,(iy+#12),h:res 1,(iy+#12),l:res 1,(iy+#12):res 1,(iy+#12),a::res 2,(iy+#12),b:" \
+ "res 2,(iy+#12),c:res 2,(iy+#12),d:res 2,(iy+#12),e:res 2,(iy+#12),h:res 2,(iy+#12),l:" \
+ "res 2,(iy+#12):res 2,(iy+#12),a::res 3,(iy+#12),b:res 3,(iy+#12),c:res 3,(iy+#12),d:" \
+ "res 3,(iy+#12),e:res 3,(iy+#12),h:res 3,(iy+#12),l:res 3,(iy+#12):res 3,(iy+#12),a::" \
+ "res 4,(iy+#12),b:res 4,(iy+#12),c:res 4,(iy+#12),d:res 4,(iy+#12),e:res 4,(iy+#12),h:" \
+ "res 4,(iy+#12),l:res 4,(iy+#12):res 4,(iy+#12),a::res 5,(iy+#12),b:res 5,(iy+#12),c:" \
+ "res 5,(iy+#12),d:res 5,(iy+#12),e:res 5,(iy+#12),h:res 5,(iy+#12),l:res 5,(iy+#12):" \
+ "res 5,(iy+#12),a::res 6,(iy+#12),b:res 6,(iy+#12),c:res 6,(iy+#12),d:res 6,(iy+#12),e:" \
+ "res 6,(iy+#12),h:res 6,(iy+#12),l:res 6,(iy+#12):res 6,(iy+#12),a::res 7,(iy+#12),b:" \
+ "res 7,(iy+#12),c:res 7,(iy+#12),d:res 7,(iy+#12),e:res 7,(iy+#12),h:res 7,(iy+#12),l:" \
+ "res 7,(iy+#12):res 7,(iy+#12),a::set 0,(iy+#12),b:set 0,(iy+#12),c:set 0,(iy+#12),d:" \
+ "set 0,(iy+#12),e:set 0,(iy+#12),h:set 0,(iy+#12),l:set 0,(iy+#12):set 0,(iy+#12),a::" \
+ "set 1,(iy+#12),b:set 1,(iy+#12),c:set 1,(iy+#12),d:set 1,(iy+#12),e:set 1,(iy+#12),h:" \
+ "set 1,(iy+#12),l:set 1,(iy+#12):set 1,(iy+#12),a::set 2,(iy+#12),b:set 2,(iy+#12),c:" \
+ "set 2,(iy+#12),d:set 2,(iy+#12),e:set 2,(iy+#12),h:set 2,(iy+#12),l:set 2,(iy+#12):" \
+ "set 2,(iy+#12),a::set 3,(iy+#12),b:set 3,(iy+#12),c:set 3,(iy+#12),d:set 3,(iy+#12),e:" \
+ "set 3,(iy+#12),h:set 3,(iy+#12),l:set 3,(iy+#12):set 3,(iy+#12),a::set 4,(iy+#12),b:" \
+ "set 4,(iy+#12),c:set 4,(iy+#12),d:set 4,(iy+#12),e:set 4,(iy+#12),h:set 4,(iy+#12),l:" \
+ "set 4,(iy+#12):set 4,(iy+#12),a::set 5,(iy+#12),b:set 5,(iy+#12),c:set 5,(iy+#12),d:" \
+ "set 5,(iy+#12),e:set 5,(iy+#12),h:set 5,(iy+#12),l:set 5,(iy+#12):set 5,(iy+#12),a::" \
+ "set 6,(iy+#12),b:set 6,(iy+#12),c:set 6,(iy+#12),d:set 6,(iy+#12),e:set 6,(iy+#12),h:" \
+ "set 6,(iy+#12),l:set 6,(iy+#12):set 6,(iy+#12),a::set 7,(iy+#12),b:set 7,(iy+#12),c:" \
+ "set 7,(iy+#12),d:set 7,(iy+#12),e:set 7,(iy+#12),h:set 7,(iy+#12),l:set 7,(iy+#12):" \
+ "set 7,(iy+#12),a:"
+
+#define AUTOTEST_LABNUM "mavar=67:label{mavar}truc:ld hl,7+2*label{mavar}truc:mnt=1234567:lab2{mavar}{mnt}:" \
+ "ld de,lab2{mavar}{mnt}:lab3{mavar}{mnt}h:ld de,lab3{mavar}{mnt}h"
+
+#define AUTOTEST_EQUNUM "mavar = 9:monlabel{mavar+5}truc:unalias{mavar+5}heu equ 50:autrelabel{unalias14heu}:ld hl,autrelabel50"
+
+#define AUTOTEST_DELAYNUM "macro test label: dw {label}: endm: repeat 3, idx:idx2 = idx-1:" \
+ " test label_{idx2}: rend:repeat 3, idx:label_{idx-1}:nop:rend"
+
+#define AUTOTEST_STRUCT "org #1000:label1 :struct male:age defb 0:height defb 0:endstruct:struct female:" \
+ "age defb 0:height defb 0:endstruct:struct couple:struct male husband:" \
+ "struct female wife:endstruct:if $-label1!=0:stop:endif:ld a,(ix+couple.wife.age):" \
+ "ld bc,couple:ld bc,{sizeof}couple:struct couple mycouple:" \
+ "if mycouple.husband != mycouple.husband.age:stop:endif:ld hl,mycouple:" \
+ "ld hl,mycouple.wife.age:ld bc,{sizeof}mycouple:macro cmplastheight p1:" \
+ "ld hl,@mymale.height:ld a,{p1}:cp (hl):ld bc,{sizeof}@mymale:ld hl,@mymale:ret:" \
+ "struct male @mymale:mend:cmplastheight 5:cmplastheight 3:nop"
+
+#define AUTOTEST_STRUCT2 "struct bite: preums defb 0: deuze defw 1: troize defs 10: endstruct:" \
+ " if {sizeof}bite.preums!=1 || {sizeof}bite.deuze!=2 || {sizeof}bite.troize!=10: stop: endif: nop "
+
+#define AUTOTEST_REPEAT "ce=100:repeat 2,ce:repeat 5,cx:repeat 5,cy:defb cx*cy:rend:rend:rend:assert cx==6 && cy==6 && ce==3:" \
+ "cpt=0:repeat:cpt=cpt+1:until cpt>4:assert cpt==5"
+
+#define AUTOTEST_REPEATKO "repeat 5:nop"
+
+#define AUTOTEST_WHILEKO "while 5:nop"
+
+#define AUTOTEST_TICKER "repeat 2: ticker start, mc:out (0),a:out (c),a:out (c),h:out (c),0:ticker stop, mc:if mc!=15:ld hl,bite:else:nop:endif:rend"
+
+#define AUTOTEST_ORG "ORG #8000,#1000:defw $:ORG $:defw $"
+
+#define AUTOTEST_BANKORG "bank 0:nop:org #5:nop:bank 1:unevar=10:bank 0:assert $==6:ret:bank 1:assert $==0:bank 0:assert $==7"
+
+#define AUTOTEST_VAREQU "label1 equ #C000:label2 equ (label1*2)/16:label3 equ label1-label2:label4 equ 15:var1=50*3+2:var2=12*label1:var3=label4-8:var4=label2:nop"
+
+#define AUTOTEST_FORMAT "hexa=#12A+$23B+45Ch+0x56D:deci=123.45+-78.54*2-(7-7)*2:bina=0b101010+1010b-%1111:assert hexa==3374 && deci==-33.63 && bina==37:nop"
+
+#define AUTOTEST_CHARSET "charset 'abcde',0:defb 'abcde':defb 'a','b','c','d','e':defb 'a',1*'b','c'*1,1*'d','e'*1:charset:" \
+ "defb 'abcde':defb 'a','b','c','d','e':defb 'a',1*'b','c'*1,1*'d','e'*1"
+
+#define AUTOTEST_CHARSET2 "charset 97,97+26,0:defb 'roua':charset:charset 97,10:defb 'roua':charset 'o',5:defb 'roua':charset 'ou',6:defb 'roua'"
+
+#define AUTOTEST_NOCODE "let monorg=$:NoCode:Org 0:Element1 db 0:Element2 dw 3:Element3 ds 50:Element4 defb 'rdd':Org 0:pouet defb 'nop':" \
+ "Code:Org monorg:cpt=$+element2+element3+element4:defs cpt,0"
+
+#define AUTOTEST_LZSEGMENT "org #100:debut:jr nz,zend:lz48:repeat 128:nop:rend:lzclose:jp zend:lz48:repeat 2:dec a:jr nz,@next:ld a,5:@next:jp debut:rend:" \
+ "lzclose:zend"
+
+#define AUTOTEST_PAGETAG "bankset 0:org #5000:label1:bankset 1:org #9000:label2:bankset 2:" \
+ "assert {page}label1==0x7FC0:assert {page}label2==0x7FC6:assert {pageset}label1==#7FC0:assert {pageset}label2==#7FC2:nop"
+
+#define AUTOTEST_PAGETAG2 "bankset 0:call maroutine:bank 4:org #C000:autreroutine:nop:" \
+ "ret:bank 5:org #8000:maroutine:ldir:ret:bankset 2:org #9000:troize:nop:" \
+ "assert {page}maroutine==#7FC5:assert {pageset}maroutine==#7FC2:assert {page}autreroutine==#7FC4:" \
+ "assert {pageset}autreroutine==#7FC2:assert {page}troize==#7FCE:assert {pageset}troize==#7FCA"
+
+#define AUTOTEST_PAGETAG3 "buildsna:bank 2:assert {bank}$==2:assert {page}$==0x7FC0:assert {pageset}$==#7FC0:" \
+ "bankset 1:org #4000:assert {bank}$==5:assert {page}$==0x7FC5:assert {pageset}$==#7FC2"
+
+#define AUTOTEST_SWITCH "mavar=4:switch mavar:case 1:nop:case 4:defb 4:case 3:defb 3:break:case 2:nop:case 4:defb 4:endswitch"
+
+#define AUTOTEST_PREPRO0 "\n\n\n\n;bitch\n\nnop\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nxor a\n\n\n\n\n\n\n\n\n\n\n\n"
+
+#define AUTOTEST_PREPRO1 " macro DEBUG_INK0, coul: out (c), a: endm: ifndef NDEBUG:DEBUG_BORDER_NOPS = 0: endif"
+
+#define AUTOTEST_PREPRO2 " nop : mycode other tartine ldir : defw tartine,other, mycode : assert tartine==other && other==mycode && mycode==1 && $==9"
+
+#define AUTOTEST_PREPRO3 "nop\n \n /* test ' ici */\n nop /* retest */ 5\n /* bonjour\n prout\n caca\n pipi */" \
+ " nop\n nop /* nop */ : nop\n ; grouik /*\n \n /* ; pouet */ ld hl,#2121\n "
+
+#define AUTOTEST_PREPRO4 "glop=0\n nop ; glop=1\n assert glop==0\n nop\n glop=0\n nop // glop=1\n assert glop==0\n nop\n glop=0 : /* glop=1 */ nop\n" \
+ "assert glop==0\n nop\n \n glop=0 : /* glop=1 // */ nop\n assert glop==0\n nop\n glop=0 : /* glop=1 ; */ nop\n" \
+ "assert glop==0\n nop\n glop=0 ; /* glop=1\n nop // glop=2 /*\n assert glop==0\n nop\n"
+
+#define AUTOTEST_PROXIM "routine:.step1:jp .step2:.step2:jp .step1:deuze:nop:.step1:djnzn .step1:djnz routine.step2"
+
+#define AUTOTEST_TAGPRINT "unevar=12:print 'trucmuche{unevar}':print '{unevar}':print '{unevar}encore','pouet{unevar}{unevar}':ret"
+
+#define AUTOTEST_TAGFOLLOW "ret:uv=1234567890:unlabel_commeca_{uv} equ pouetpouetpouettroulala:pouetpouetpouettroulala:assert unlabel_commeca_{uv}>0"
+
+#define AUTOTEST_TAGREALLOC "zoomscroller_scroller_height equ 10: another_super_long_equ equ 256: pinouille_super_long_useless_equ equ 0: " \
+ "zz equ 1111111111: org #100: repeat zoomscroller_scroller_height,idx: " \
+ "zoomscroller_buffer_line_{idx-1}_{pinouille_super_long_useless_equ} nop: zoomscroller_buffer_line_{idx-1}_{zz} nop: " \
+ "rend: align 256: repeat zoomscroller_scroller_height,idx: zoomscroller_buffer_line_{idx-1}_{pinouille_super_long_useless_equ}_duplicate nop: " \
+ "zoomscroller_buffer_line_{idx-1}_{zz}_duplicate nop: rend: repeat zoomscroller_scroller_height, line: idx = line - 1: " \
+ "assert zoomscroller_buffer_line_{idx}_{pinouille_super_long_useless_equ} + another_super_long_equ == "\
+ "zoomscroller_buffer_line_{idx}_{pinouille_super_long_useless_equ}_duplicate: " \
+ "assert zoomscroller_buffer_line_{idx}_{zz} + another_super_long_equ == zoomscroller_buffer_line_{idx}_{zz}_duplicate: rend"
+
+#define AUTOTEST_DEFUSED "ld hl,labelused :ifdef labelused:fail 'labelexiste':endif:ifndef labelused:else:fail 'labelexiste':endif:" \
+ "ifnused labelused:fail 'labelused':endif:ifused labelused:else:fail 'labelused':endif:labelused"
+
+#define AUTOTEST_SAVEINVALID1 "nop : save'gruik',20,-100"
+
+#define AUTOTEST_SAVEINVALID2 "nop : save'gruik',-20,100"
+
+#define AUTOTEST_SAVEINVALID3 "nop : save'gruik',40000,30000"
+
+#define AUTOTEST_MACROPROX " macro unemacro: nop: endm: global_label: ld hl, .table: .table"
+
+#define AUTOTEST_PROXBACK " macro grouik: @truc djnz @truc: .unprox djnz .unprox: mend:" \
+ "ld b,2: unglobal nop: djnz unglobal: ld b,2: .unprox nop: djnz .unprox: beforelocal=$ :" \
+ "repeat 2: @unlocal: ld b,2: .unprox nop: djnz .unprox: grouik : djnz .unprox : rend: assert .unprox < beforelocal : nop"
+#define AUTOTEST_LOCAPROX "repeat 1: @label nop: .prox nop: @label2 nop: djnz @label.prox: rend"
+
+#define AUTOTEST_QUOTES "defb 'rdd':str 'rdd':charset 'rd',0:defb '\\r\\d':str '\\r\\d'"
+
+#define AUTOTEST_NEGATIVE "ld a,-5: ld bc,-0x3918:ld de,0+-#3918:ld de,-#3918:var1=-5+6:var2=-#3918+#3919:assert var1+var2==2"
+
+#define AUTOTEST_FORMULA1 "a=5:b=2:assert int(a/b)==3:assert !a+!b==0:a=a*100:b=b*100:assert a*b==100000:ld hl,a*b-65536:a=123+-5*(-6/2)-50*2<<1"
+
+#define AUTOTEST_FORMULA2 "vala= (0.5+(4*0.5))*6:valb= int((0.5+(4*0.5))*6):nop:if vala!=valb:push erreur:endif"
+
+#define AUTOTEST_SHIFTMAX "a=45: a=a>>256: assert a==0:nop"
+
+#define AUTOTEST_FRAC "mavar=frac(5.5):assert mavar==0.5:assert frac(6.6)==0.6:assert frac(1.1)==0.1:assert frac(1)==0:assert frac(100000)==0:nop"
+
+/* test override control between bank and bankset in snapshot mode + temp workspace */
+#define AUTOTEST_BANKSET "buildsna:bank 0:nop:bank 1:nop:bank:nop:bank 2:nop:bank 3:nop:bankset 1:nop:bank 8:nop:bank 9:nop:bank 10:nop:bank 11:nop"
+
+#define AUTOTEST_LIMITOK "org #100:limit #102:nop:limit #103:ld a,0:protect #105,#107:limit #108:xor a:org $+3:inc a"
+
+#define AUTOTEST_LIMITKO "limit #100:org #100:add ix,ix"
+
+#define AUTOTEST_DEFS "defs 256,0"
+
+#define AUTOTEST_LIMIT03 "limit -1 : nop"
+#define AUTOTEST_LIMIT04 "limit #10000 : nop"
+#define AUTOTEST_LIMIT05 "org #FFFF : ldir"
+#define AUTOTEST_LIMIT06 "org #FFFF : nop"
+
+#define AUTOTEST_LIMIT07 "org #ffff : Start: equ $ : di : ld hl,#c9fb : ld (#38),hl"
+
+#define AUTOTEST_DELAYED_RUN "run _start:nop:_start nop"
+
+#define AUTOTEST_INHIBITION "if 0:ifused truc:ifnused glop:ifdef bidule:ifndef machin:ifnot 1:nop:endif:nop:else:nop:endif:endif:endif:endif:endif"
+
+#define AUTOTEST_LZ4 "lz4:repeat 10:nop:rend:defb 'roudoudoudouoneatxkjhgfdskljhsdfglkhnopnopnopnop':lzclose"
+
+#define AUTOTEST_MAXERROR "repeat 20:aglapi:rend:nop"
+
+#define AUTOTEST_ENHANCED_LD "ld h,(ix+11): ld l,(ix+10): ld h,(iy+21): ld l,(iy+20): ld b,(ix+11): ld c,(ix+10):" \
+ "ld b,(iy+21): ld c,(iy+20): ld d,(ix+11): ld e,(ix+10): ld d,(iy+21): ld e,(iy+20): ld hl,(ix+10): " \
+ "ld hl,(iy+20):ld bc,(ix+10):ld bc,(iy+20): ld de,(ix+10):ld de,(iy+20)"
+
+#define AUTOTEST_ENHANCED_PUSHPOP "push bc,de,hl,ix,iy,af:pop hl,bc,de,iy,ix,af:nop 2:" \
+ "push bc:push de:push hl:push ix:push iy:push af:"\
+ "pop hl:pop bc:pop de:pop iy:pop ix:pop af:nop:nop"
+#define AUTOTEST_ENHANCED_LD2 "ld (ix+0),hl: ld (ix+0),de: ld (ix+0),bc: ld (iy+0),hl: ld (iy+0),de: ld (iy+0),bc:"\
+"ld (ix+1),h: ld (ix+0),l: ld (ix+1),d: ld (ix+0),e: ld (ix+1),b: ld (ix+0),c: ld (iy+1),h: ld (iy+0),l: ld (iy+1),d: ld (iy+0),e: ld (iy+1),b: ld (iy+0),c"
+
+#define AUTOTEST_INHIBITION2 "ifdef roudoudou:macro glop bank,page,param:ld a,{bank}:ld hl,{param}{bank}:if {bank}:nop:else:exx:" \
+ "endif::switch {param}:nop:case 4:nop:case {param}:nop:default:nop:break:endswitch:endif:defb 'coucou'"
+
+#define AUTOTEST_INHIBITIONMAX "roudoudou:ifndef roudoudou:if pouet:macro glop bank,page,param:ifdef nanamouskouri:ld hl,{param}{bank}:"\
+ "elseif aglapi:exx:endif:if {bank}:nop:elseif {grouik}:exx:endif:switch {bite}:nop:case {nichon}:nop:default:nop:break:endswitch:else:"\
+ "ifnot {jojo}:exx:endif:endif:else:defb 'coucou':endif"
+
+#define AUTOTEST_NOEXPORT "unlabel nop:.unlocal nop:unevar=5:unequ equ 10:noexport unlabel, .unlocal, "\
+ "unlabel.unlocal, unevar, unequ:enoexport unlabel, .unlocal, unlabel.unlocal, unevar, unequ"
+
+#define AUTOTEST_UNDERVAR "coucou nop:_coucou nop:_mavar=5:jr _coucou+_mavar:print _mavar"
+
+#define AUTOTEST_CODESKIP "org #100: nop: old_dollar=$: nocode: defs 10: assert $==old_dollar+10: code: "\
+ "assert $==old_dollar+10: org #200: nop: old_dollar=$: nocode: defs 10: assert $==old_dollar+10: code skip: assert $==old_dollar"
+
+#define AUTOTEST_EMBEDDED_ERRORS "nop : rien : rien : rien : glop nop : glapi nop"
+
+#define AUTOTEST_EMBEDDED_LABELS " disarkCounter = 0:MACRO dkps:PLY_AKG_DisarkPointerRegionStart_{disarkCounter}:ENDM" \
+ ":MACRO dkpe\nPLY_AKG_DisarkPointerRegionEnd_{disarkCounter}:\ndisarkCounter = disarkCounter + 1:ENDM:\ndkps\ndkpe\ndkps"
+
+#define AUTOTEST_TAGLOOP "tab1 = #100:tab2 = #200:tab3 = #300:macro genreg channel,psg:label{channel} EQU tab{psg} + channeloffset:"\
+ "endm:channel=1:while channel <= 9:psg=floor(((channel-1)/3)+1):channeloffset=((channel-1) % 3)*2:"\
+ "genreg {channel},{psg}:channel=channel+1:wend:assert LABEL5==#0202:assert LABEL6==#0204:assert LABEL7==#0300:"\
+ "assert LABEL8==#0302:nop"
+
+#define AUTOTEST_PLUSCOLOR " myval=0x123 : assert getr(myval)==2 : assert getb(myval)==3 : assert getg(myval)==1 : assert getv(myval)==1" \
+ " : assert setr(2)+setv(1)+setb(3)==0x123 : assert setv(-2)==setv(0) && setb(220)==setb(15) : nop "
+
+#define AUTOTEST_ASSERT " macro zem,nom,adr: overflow equ $-{adr}: assert $<{adr},{nom}: nop: mend: zem 'bidule',#2FFF :" \
+ " macro zem2,nom,adr: overflow2 equ $-{adr}: assert $<{adr},{nom},{adr},overflow2: nop: mend: zem2 'bidule',#2FFF :" \
+ " macro zem3,nom,adr: overflow3 equ $-{adr}: assert $<{adr},{nom},{hex}{adr},overflow3: nop: mend: zem3 'bidule',#2FFF "
+
+#define AUTOTEST_OPERATORASSIGN "a=5 : a*=3 : assert a==15 : a/=5 : assert a==3 : a&=2 : assert a==2 : a+=10 : assert a==12 : "\
+ "a-=3 : assert a==9 : a%=5 : assert a==4 : a|=1 : assert a==5 : a<<=3 : assert a==40 : "\
+ "a>>=1 : assert a==20 : nop"
+#define AUTOTEST_OPERATORASSIGN2 "a=1 : a+=2 : assert a==3 : repeat 2 : a+=10 : rend : assert a==23: a=1 : a-=2 : assert a==-1 : repeat 2 : a-=10 : rend : assert a==-21: " \
+ "a=3 : a*=2 : assert a==6 : repeat 2 : a*=10 : rend : assert a==600: a=600 : a/=2 : assert a==300 : repeat 2 : a/=10 : rend : assert a==3 : nop"
+
+
+
+struct s_autotest_keyword {
+ char *keywordtest;
+ int result;
+};
+
+struct s_autotest_keyword autotest_keyword[]={
+ {"ld ly,h",1}, {"ld lx,h",1}, {"ld ly,l",1}, {"ld lx,l",1}, {"ld hy,h",1}, {"ld hx,h",1}, {"ld hy,l",1}, {"ld hx,l",1},
+ {"nop",0},{"nop 2",0},{"nop a",1},{"nop (hl)",1},{"nop nop",1},{"nop grouik",1},
+ {"ldir",0},{"ldir 5",1},{"ldir ldir",1},{"ldir (hl)",1},{"ldir a",1},{"ldir grouik",1},
+ {"ldi",0},{"ldi 5",1},{"ldi ldi",1},{"ldi (hl)",1},{"ldi a",1},{"ldi grouik",1},
+ {"lddr",0},{"lddr 5",1},{"lddr lddr",1},{"lddr (hl)",1},{"lddr a",1},{"lddr groudk",1},
+ {"ldd",0},{"ldd 5",1},{"ldd ldd",1},{"ldd (hl)",1},{"ldd a",1},{"ldd groudk",1},
+ {"jr $",0},{"jr 0",0},{"jr jr",1},{"jr (hl)",1},{"jr a",1},
+ {"jr c,$",0},{"jr c,0",0},{"jr c,jr",1},{"jr c,(hl)",1},{"jr c,a",1},
+ {"jr nc,$",0},{"jr nc,0",0},{"jr nc,jr",1},{"jr nc,(hl)",1},{"jr nc,a",1},
+ {"jr z,$",0},{"jr 0",0},{"jr jr",1},{"jr (hl)",1},{"jr a",1},
+ {"jr nz,$",0},{"jr 0",0},{"jr jr",1},{"jr (hl)",1},{"jr a",1},
+ {"jp $",0},{"jp 0",0},{"jp jp",1},{"jp (hl)",0},{"jp (ix)",0},{"jp (iy)",0},{"jp (de)",1},{"jp a",1},
+ {"jp c,$",0},{"jp c,0",0},{"jp c,jp",1}, {"jp c,(hl)",1}, {"jp c,(ix)",1}, {"jp c,(iy)",1},{"jp c,(de)",1},{"jp c,a",1},
+ {"jp nc,$",0},{"jp nc,0",0},{"jp nc,jp",1},{"jp nc,(hl)",1},{"jp nc,(ix)",1},{"jp nc,(iy)",1},{"jp nc,(de)",1},{"jp nc,a",1},
+ {"jp z,$",0},{"jp z,0",0},{"jp z,jp",1}, {"jp z,(hl)",1}, {"jp z,(ix)",1}, {"jp z,(iy)",1},{"jp z,(de)",1},{"jp z,a",1},
+ {"jp nz,$",0},{"jp nz,0",0},{"jp nz,jp",1},{"jp nz,(hl)",1},{"jp nz,(ix)",1},{"jp nz,(iy)",1},{"jp nz,(de)",1},{"jp nz,a",1},
+ {"jp pe,$",0},{"jp pe,0",0},{"jp pe,jp",1},{"jp pe,(hl)",1},{"jp pe,(ix)",1},{"jp pe,(iy)",1},{"jp pe,(de)",1},{"jp pe,a",1},
+ {"jp po,$",0},{"jp po,0",0},{"jp po,jp",1},{"jp po,(hl)",1},{"jp po,(ix)",1},{"jp po,(iy)",1},{"jp po,(de)",1},{"jp po,a",1},
+ {"jp p,$",0},{"jp p,0",0},{"jp p,jp",1}, {"jp p,(hl)",1}, {"jp p,(ix)",1}, {"jp p,(iy)",1},{"jp p,(de)",1},{"jp p,a",1},
+ {"jp m,$",0},{"jp m,0",0},{"jp m,jp",1}, {"jp m,(hl)",1}, {"jp m,(ix)",1}, {"jp m,(iy)",1},{"jp m,(de)",1},{"jp m,a",1},
+ {"ret",0},{"ret c",0},{"ret nc",0},{"ret pe",0},{"ret po",0},{"ret m",0},{"ret p",0},{"reti",0},{"ret ret",1},{"ret 5",1},{"ret (hl)",1},{"ret a",1},
+ {"xor a",0},{"xor a,b",1},{"xor",1},{"xor (de)",1},{"xor (hl)",0},{"xor (bc)",1},{"xor (ix+0)",0},{"xor (iy+0)",},{"xor xor",1},
+ {"and a",0},{"and a,b",1},{"xor",1},{"and (de)",1},{"and (hl)",0},{"and (bc)",1},{"and (ix+0)",0},{"and (iy+0)",},{"and xor",1},
+ {"or a",0},{"or a,b",1},{"xor",1},{"or (de)",1},{"or (hl)",0},{"or (bc)",1},{"or (ix+0)",0},{"or (iy+0)",},{"or xor",1},
+ {"add",1},{"add a",0},{"add a,a",0},{"add add",1},{"add (hl)",0},{"add (de)",1},{"add xh",0},{"add grouik",1},
+ {"add hl,ix",1},{"add hl,iy",1},{"add ix,iy",1},{"add iy,ix",1},{"add hl,0",1},{"add hl,grouik",1},{"add ix,hl",1},{"add iy,hl",1},
+ {"adc",1},{"adc a",0},{"adc a,a",0},{"adc adc",1},{"adc (hl)",0},{"adc (de)",1},{"adc xh",0},{"adc grouik",1},
+ {"adc hl,ix",1},{"adc hl,iy",1},{"adc ix,iy",1},{"adc iy,ix",1},{"adc hl,0",1},{"adc hl,grouik",1},{"adc ix,hl",1},{"adc iy,hl",1},
+ {"sub",1},{"sub a",0},{"sub a,a",0},{"sub sub",1},{"sub (hl)",0},{"sub (de)",1},{"sub xh",0},{"sub grouik",1},
+ {"sub hl,ix",1},{"sub hl,iy",1},{"sub ix,iy",1},{"sub iy,ix",1},{"sub hl,0",1},{"sub hl,grouik",1},{"sub ix,hl",1},{"sub iy,hl",1},
+ {"sbc",1},{"sbc a",0},{"sbc a,a",0},{"sbc sbc",1},{"sbc (hl)",0},{"sbc (de)",1},{"sbc xh",0},{"sbc grouik",1},
+ {"sbc hl,ix",1},{"sbc hl,iy",1},{"sbc ix,iy",1},{"sbc iy,ix",1},{"sbc hl,0",1},{"sbc hl,grouik",1},{"sbc ix,hl",1},{"sbc iy,hl",1},
+ {"exx",0},{"exx hl",1},{"exx hl,de",1},{"exx af,af'",1},{"exx exx",1},{"exx 5",1},
+ {"ex",1},{"ex af,af'",0},{"ex hl,de",0},{"ex hl,bc",1},{"ex hl,hl",1},{"ex hl,ix",1},
+ {"cp",1},{"cp cp ",1},{"cp 5",0},{"cp c",0},{"cp a,5",0},{"cp a,c",0},{"cp hl",1},{"cp (hl)",0},{"cp a,(hl)",0},{"cp (de)",1},{"cp de",1},
+ {"cpi",0},{"cpi (hl)",1},{"cpi a",1},{"cpi 5",1},
+ {"cpd",0},{"cpd (hl)",1},{"cpd a",1},{"cpd 5",1},
+ {"cpir",0},{"cpir (hl)",1},{"cpir a",1},{"cpir 5",1},
+ {"cpdr",0},{"cpdr (hl)",1},{"cpdr a",1},{"cpdr 5",1},
+ {"call #1234",0},{"call call",1},{"call (hl)",1},{"call (ix)",1},{"call (iy)",1},{"call (de)",1},{"call hl",1},{"call bc",1},{"call a",1},{"call 5,5",1},
+ {"rst 5",1},{"rst",1},{"rst 0",0},{"rst rst",1},{"rst (hl)",1},{"rst (ix)",1},{"rst (iy)",1},{"rst z",1},{"rst z,0",1},
+ {"djnz",1},{"djnz $",0},{"djnz $,0",1},{"djnz djnz",1},{"djnz (hl)",1},
+ {"djnz (ix)",1},{"djnz (iy)",1},{"djnz (bc)",1},{"djnz bc",1},{"djnz ix",1},{"djnz iy",1},{"djnz hl",1},
+ {"push",1},{"push push",1},{"push pop",1},{"push af'",1},{"push (ix)",1},{"push (hl)",1},{"push (#1234)",1},{"push #1234",1},
+ {"pop",1},{"pop pop",1},{"pop push",1},{"pop af'",1},{"pop (ix)",1},{"pop (hl)",1},{"pop (#1234)",1},{"pop #1234",1},
+ {"set -1,a",1},{"set 9,a",1},{"set 0,xh",1},{"set 0,ix",1},{"set 0",1},{"set",1},{"set set",1},{"set 0,a,a",1},{"set 0,(ix+0),xh",1},
+ {"bit -1,a",1},{"bit 9,a",1},{"bit 0,xh",1},{"bit 0,ix",1},{"bit 0",1},{"bit",1},{"bit bit",1},{"bit 0,a,a",1},{"bit 0,(ix+0),xh",1},
+ {"res -1,a",1},{"res 9,a",1},{"res 0,xh",1},{"res 0,ix",1},{"res 0",1},{"res",1},{"res res",1},{"res 0,a,a",1},{"res 0,(ix+0),xh",1},
+ {"srl",1},{"srl srl",1},{"srl hl",0}, /* srl hl is a kind of macro */
+ {"rld a",1},{"rld (hl)",1},{"rld rld",1},{"rld 5",1},{"rld (ix)",1},
+ {"rrd a",1},{"rrd (hl)",1},{"rrd rrd",1},{"rrd 5",1},{"rrd (ix)",1},
+ {"cpl a",1},{"cpl (hl)",1},{"cpl cpl",1},{"cpl 0",1},
+ {"daa daa daa",1},{"daa 0",1},{"daa (hl)",1},
+ {"scf scf",1},{"scf 0",1},{"scf (hl)",1},
+ {"ccf ccf",1},{"ccf 0",1},{"ccf (hl)",1},
+ {"out",1},{"out out",1},{"out (c)",1},{"out (c),xh",1},{"out 0",1},
+ {"out (c),hl",1},{"out (hl),c",1},{"out (c),(ix+0)",1},{"out (c),a,b",1},
+ {"outi 0",1},{"outi (hl)",1},
+ {"otir 0",1},{"otir (hl)",1},
+ {"otdr 0",1},{"otdr (hl)",1},
+ {"outd 0",1},{"outd (hl)",1},
+ {"in",1},{"in in",1},{"in (c)",1},{"in xh,(c)",1},{"in 0",1},
+ {"in hl,(c)",1},{"in c,(hl)",1},{"in (c),(ix+0)",1},{"in a,(c),b",1},
+ {"ini 0",1},{"ini (hl)",1},
+ {"inir 0",1},{"inir (hl)",1},
+ {"indr 0",1},{"indr (hl)",1},
+ {"ind 0",1},{"ind (hl)",1},
+ {"di 5",1},{"di di",1},{"di hl",1},{"di a",1},
+ {"ei 5",1},{"ei ei",1},{"ei hl",1},{"ei a",1},
+ {"im",1},{"im 3",1},{"im -1",1},{"im (hl)",1},
+ {"halt 5",1},{"reti 5",1},{"retn 5",1},{"ld i,b",1},{"ld b,i",1},
+
+ {"repeat 5:nop:rend",0},{"repeat 100000:a=5:rend",1},{"repeat -5:nop:rend",1},{"repeat repeat:nop:rend",1},
+ {"macro bidule:nop:mend:bidule",0},{"macro bidule:nop:macro glop:nop:mend:mend:bidule",1},
+ {"macro bidule:nop",1},{"macro bidule:nop:mend:macro glop:nop:bidule",1},
+ /*
+ {"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},
+ {"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},
+ {"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},
+ */
+ {NULL,0}
+};
+
+void MiniDump(unsigned char *opcode, int opcodelen) {
+ #undef FUNC
+ #define FUNC "MiniDump"
+
+ int i;
+ printf("%d byte%s to dump\n",opcodelen,opcodelen>1?"s":"");
+ for (i=0;i<opcodelen;i++) {
+ printf("%02X \n",opcode[i]);
+ }
+ printf("\n");
+}
+
+void RasmAutotest(void)
+{
+ #undef FUNC
+ #define FUNC "RasmAutotest"
+
+ struct s_rasm_info *debug;
+ unsigned char *opcode=NULL;
+ int opcodelen,ret,filelen;
+ int cpt=0,chk,i,idx;
+ char tmpstr1[256],tmpstr2[256],*tmpstr3,**tmpsplit;
+
+#ifdef RDD
+ printf("\n%d bytes\n",_static_library_memory_used);
+#endif
+#if 0
+ /* Autotest CORE */
+ #ifdef OS_WIN
+ printf(".");fflush(stdout);
+ strcpy(tmpstr1,".\\archives\\job.asm");
+ strcpy(tmpstr2,".\\archives\\job.asm");
+ SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath0) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);}
+ strcpy(tmpstr1,".\\archives\\..\\job.asm");
+ strcpy(tmpstr2,".\\job.asm");
+ SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath1) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);}
+ strcpy(tmpstr1,".\\archives\\gruik\\..\\..\\job.asm");
+ strcpy(tmpstr2,".\\job.asm");
+ SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath2) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);}
+ strcpy(tmpstr1,".\\archives\\..\\gruik\\..\\job.asm");
+ strcpy(tmpstr2,".\\job.asm");
+ SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath3) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);}
+ strcpy(tmpstr1,"..\\archives\\job.asm");
+ strcpy(tmpstr2,"..\\archives\\job.asm");
+ SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath4) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);}
+ strcpy(tmpstr1,".\\src\\..\\..\\src\\job.asm");
+ strcpy(tmpstr2,"..\\src\\job.asm");
+ SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath5) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);}
+ strcpy(tmpstr1,"src\\..\\..\\src\\job.asm");
+ strcpy(tmpstr2,"..\\src\\job.asm");
+ SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath6) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);}
+ cpt++;
+ #else
+ printf(".");fflush(stdout);
+ strcpy(tmpstr1,"./archives/job.asm");
+ strcpy(tmpstr2,"./archives/job.asm");
+ SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath0) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);}
+ strcpy(tmpstr1,"./archives/../job.asm");
+ strcpy(tmpstr2,"./job.asm");
+ SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath1) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);}
+ strcpy(tmpstr1,"./archives/gruik/../../job.asm");
+ strcpy(tmpstr2,"./job.asm");
+ SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath2) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);}
+ strcpy(tmpstr1,"./archives/../gruik/../job.asm");
+ strcpy(tmpstr2,"./job.asm");
+ SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath3) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);}
+ strcpy(tmpstr1,"../archives/job.asm");
+ strcpy(tmpstr2,"../archives/job.asm");
+ SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath4) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);}
+ strcpy(tmpstr1,"./src/../../src/job.asm");
+ strcpy(tmpstr2,"../src/job.asm");
+ SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath5) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);}
+ strcpy(tmpstr1,"src/../../../src/job.asm");
+ strcpy(tmpstr2,"../../src/job.asm");
+ SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath6) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);}
+ cpt++;
+ printf(".");fflush(stdout);
+ if (strcmp(GetPath("/home/roudoudou/"),"/home/roudoudou/")) {printf("Autotest %03d ERROR (Core:GetPath0) [%s]\n",cpt,GetPath("/home/roudoudou/"));exit(-1);}
+ if (strcmp(GetPath("/home/roudoudou"),"/home/")) {printf("Autotest %03d ERROR (Core:GetPath1) [%s]\n",cpt,GetPath("/home/roudoudou"));exit(-1);}
+ cpt++;
+ #endif
+#endif
+
+ /* Autotest preprocessing */
+ ret=RasmAssemble(AUTOTEST_VIRGULE,strlen(AUTOTEST_VIRGULE),&opcode,&opcodelen);
+ if (ret) {} else {printf("Autotest %03d ERROR (double comma must trigger an error) ret=%d\n",cpt,ret);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing preprocessor comma management in a string OK\n");
+
+ ret=RasmAssemble(AUTOTEST_VIRGULE2,strlen(AUTOTEST_VIRGULE2),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (double comma in a string must be OK)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing preprocessor comma management in a string OK\n");
+
+ ret=RasmAssemble(AUTOTEST_PREPRO0,strlen(AUTOTEST_PREPRO0),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (uninitialize memory read!)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing preprocessor non regression OK\n");
+
+ ret=RasmAssemble(AUTOTEST_PREPRO1,strlen(AUTOTEST_PREPRO1),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (freestyle case 1)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing preprocessor freestyle OK\n");
+
+ ret=RasmAssemble(AUTOTEST_PREPRO2,strlen(AUTOTEST_PREPRO2),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (freestyle case 2)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing preprocessor freestyle variation 2 OK\n");
+
+ ret=RasmAssemble(AUTOTEST_PREPRO3,strlen(AUTOTEST_PREPRO3),&opcode,&opcodelen);
+ if (!ret && opcodelen==12 && opcode[11]==0x21) {} else {printf("Autotest %03d ERROR (multi-line comment)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing multi-line comments 1 OK\n");
+
+ ret=RasmAssemble(AUTOTEST_PREPRO4,strlen(AUTOTEST_PREPRO4),&opcode,&opcodelen);
+ if (!ret && opcodelen==12) {} else {printf("Autotest %03d ERROR (multi-line comment 2)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing multi-line comments 2 OK\n");
+
+ ret=RasmAssemble(AUTOTEST_OPERATOR_CONVERSION,strlen(AUTOTEST_OPERATOR_CONVERSION),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (maxam operator conversion)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing Maxam operator conversion OK\n");
+
+ ret=RasmAssemble(AUTOTEST_OPERATOR_MODULO,strlen(AUTOTEST_OPERATOR_MODULO),&opcode,&opcodelen);
+ if (!ret && opcodelen==1) {} else {printf("Autotest %03d ERROR (modulo operator conversion)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing modulo operator conversion OK\n");
+
+ ret=RasmAssemble(AUTOTEST_NOINCLUDE,strlen(AUTOTEST_NOINCLUDE),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (include missing file)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("include on a missing file OK\n");
+
+ ret=RasmAssemble(AUTOTEST_FORMAT,strlen(AUTOTEST_FORMAT),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (digit formats)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing digit formats OK\n");
+
+ /* Autotest assembling */
+ ret=RasmAssemble(AUTOTEST_OPCODES,strlen(AUTOTEST_OPCODES),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (all opcodes)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing all opcodes OK\n");
+
+ /* Autotest single instruction writes that must failed */
+ tmpstr3=TxtStrDup(AUTOTEST_INSTRMUSTFAILED);
+ tmpsplit=TxtSplitWithChar(tmpstr3,':');
+
+ for (i=0;tmpsplit[i];i++) {
+ ret=RasmAssemble(tmpsplit[i],strlen(tmpsplit[i]),&opcode,&opcodelen);
+ if (ret) {} else {printf("Autotest %03d ERROR (opcodes that must fail) -> [%s]\n",cpt,tmpsplit[i]);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;
+ }
+
+ MemFree(tmpstr3);FreeFields(tmpsplit);
+ cpt++;
+printf("testing various opcode tests OK\n");
+
+ idx=0;
+ while (autotest_keyword[idx].keywordtest) {
+ ret=RasmAssemble(autotest_keyword[idx].keywordtest,strlen(autotest_keyword[idx].keywordtest),&opcode,&opcodelen);
+ if (!ret && !autotest_keyword[idx].result) {
+ } else if (ret && autotest_keyword[idx].result) {
+ } else {
+ printf("Autotest %03d ERROR ([%s] test) is %s instead of %s\n",cpt,autotest_keyword[idx].keywordtest,!ret?"ok":"ko",ret?"ok":"ko");
+ }
+ if (opcode) MemFree(opcode);opcode=NULL;
+ idx++;
+ }
+ cpt++;
+printf("testing moar various opcode tests OK\n");
+
+ ret=RasmAssemble(AUTOTEST_ORG,strlen(AUTOTEST_ORG),&opcode,&opcodelen);
+ if (!ret && opcodelen==4 && opcode[1]==0x80 && opcode[2]==2 && opcode[3]==0x10) {} else {printf("Autotest %03d ERROR (ORG relocation)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing ORG relocation OK\n");
+
+ ret=RasmAssemble(AUTOTEST_MAXERROR,strlen(AUTOTEST_MAXERROR),&opcode,&opcodelen);
+ if (ret) {} else {printf("Autotest %03d ERROR (must return an error code!)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing error code OK\n");
+
+ ret=RasmAssemble(AUTOTEST_BANKORG,strlen(AUTOTEST_BANKORG),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (BANK org adr)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing BANK/ORG OK\n");
+
+ ret=RasmAssemble(AUTOTEST_LIMITOK,strlen(AUTOTEST_LIMITOK),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (limit ok)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing LIMIT 1 OK\n");
+
+ ret=RasmAssemble(AUTOTEST_LIMITKO,strlen(AUTOTEST_LIMITKO),&opcode,&opcodelen);
+ if (ret) {} else {printf("Autotest %03d ERROR (out of limit)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing LIMIT 2 OK\n");
+
+ ret=RasmAssemble(AUTOTEST_LIMIT03,strlen(AUTOTEST_LIMIT03),&opcode,&opcodelen);
+ if (ret) {} else {printf("Autotest %03d ERROR (limit: negative limit)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing inegative LIMIT OK\n");
+
+ ret=RasmAssemble(AUTOTEST_LIMIT04,strlen(AUTOTEST_LIMIT04),&opcode,&opcodelen);
+ if (ret) {} else {printf("Autotest %03d ERROR (limit: max limit test)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing max LIMIT OK\n");
+
+ ret=RasmAssemble(AUTOTEST_LIMIT05,strlen(AUTOTEST_LIMIT05),&opcode,&opcodelen);
+ if (ret) {} else {printf("Autotest %03d ERROR (limit: ldir in #FFFF)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing 16 bits opcode overriding LIMIT OK\n");
+
+ ret=RasmAssemble(AUTOTEST_LIMIT06,strlen(AUTOTEST_LIMIT06),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (limit: nop in #FFFF)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing opcode overriding LIMIT OK\n");
+
+ ret=RasmAssemble(AUTOTEST_LIMIT07,strlen(AUTOTEST_LIMIT07),&opcode,&opcodelen);
+ if (ret) {} else {printf("Autotest %03d ERROR (limit: ld hl,#1234 in #FFFF)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing opcode with variable overriding LIMIT OK\n");
+
+ ret=RasmAssemble(AUTOTEST_DELAYED_RUN,strlen(AUTOTEST_DELAYED_RUN),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (delayed RUN set)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing delayed RUN OK\n");
+
+ ret=RasmAssemble(AUTOTEST_LZSEGMENT,strlen(AUTOTEST_LZSEGMENT),&opcode,&opcodelen);
+ if (!ret && opcodelen==23 && opcode[1]==21 && opcode[9]==23) {} else {printf("Autotest %03d ERROR (LZ segment relocation)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing LZ segment relocation OK\n");
+
+ ret=RasmAssemble(AUTOTEST_LZ4,strlen(AUTOTEST_LZ4),&opcode,&opcodelen);
+ if (!ret && opcodelen==49 && opcode[0]==0x15 && opcode[4]==0x44 && opcode[0xB]==0xF0) {} else {printf("Autotest %03d ERROR (LZ4 segment)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing LZ4 segment OK\n");
+
+ ret=RasmAssemble(AUTOTEST_DEFS,strlen(AUTOTEST_DEFS),&opcode,&opcodelen);
+ if (!ret && opcodelen==256 && opcode[0]==0) {} else {printf("Autotest %03d ERROR (defs)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing DEFS OK\n");
+
+ ret=RasmAssemble(AUTOTEST_BANKSET,strlen(AUTOTEST_BANKSET),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (bank/bankset)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing BANK/BANKSET OK\n");
+
+ ret=RasmAssemble(AUTOTEST_PAGETAG,strlen(AUTOTEST_PAGETAG),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (page/pageset)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing prefix PAGE/PAGESET 1 OK\n");
+
+ ret=RasmAssemble(AUTOTEST_PAGETAG2,strlen(AUTOTEST_PAGETAG2),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (page/pageset 2)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing prefix PAGE/PAGESET 2 OK\n");
+
+ ret=RasmAssemble(AUTOTEST_PAGETAG3,strlen(AUTOTEST_PAGETAG3),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (page/pageset 3)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing prefix PAGE/PAGESET 3 OK\n");
+
+ ret=RasmAssemble(AUTOTEST_UNDEF,strlen(AUTOTEST_UNDEF),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (simple undef)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing UNDEF OK\n");
+
+ ret=RasmAssemble(AUTOTEST_TAGPRINT,strlen(AUTOTEST_TAGPRINT),&opcode,&opcodelen);
+ if (!ret && opcodelen==1 && opcode[0]==0xC9) {} else {printf("Autotest %03d ERROR (tag inside printed string)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing tags inside printed string OK\n");
+
+ ret=RasmAssemble(AUTOTEST_TAGFOLLOW,strlen(AUTOTEST_TAGFOLLOW),&opcode,&opcodelen);
+ if (!ret && opcodelen==1 && opcode[0]==0xC9) {} else {printf("Autotest %03d ERROR (tag+alias fast translating)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing tag+alias in fast translate OK\n");
+
+ ret=RasmAssemble(AUTOTEST_TAGREALLOC,strlen(AUTOTEST_TAGREALLOC),&opcode,&opcodelen);
+ if (!ret && opcodelen==276) {} else {printf("Autotest %03d ERROR (tag realloc with fast translate)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing tag realloc with fast translate OK\n");
+
+ ret=RasmAssemble(AUTOTEST_TAGLOOP,strlen(AUTOTEST_TAGLOOP),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (generated alias inside loop with generated var names )\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing generated alias inside loop with generated var names OK\n");
+
+ ret=RasmAssemble(AUTOTEST_PRINTVAR,strlen(AUTOTEST_PRINTVAR),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (param inside printed string)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing param inside printed string OK\n");
+
+ ret=RasmAssemble(AUTOTEST_PRINTSPACE,strlen(AUTOTEST_PRINTSPACE),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (space inside tag string for PRINT directive)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing space inside tag string for PRINT directive OK\n");
+
+ ret=RasmAssemble(AUTOTEST_INHIBITION,strlen(AUTOTEST_INHIBITION),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (conditionnal inhibition)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing conditionnal inhibition OK\n");
+
+ ret=RasmAssemble(AUTOTEST_SWITCH,strlen(AUTOTEST_SWITCH),&opcode,&opcodelen);
+ if (!ret && opcodelen==3 && opcode[0]==4 && opcode[1]==3 && opcode[2]==4) {} else {printf("Autotest %03d ERROR (switch case)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing SWITCH/CASE OK\n");
+
+ ret=RasmAssemble(AUTOTEST_NOCODE,strlen(AUTOTEST_NOCODE),&opcode,&opcodelen);
+ if (!ret && opcodelen==57) {} else {printf("Autotest %03d ERROR (code/nocode)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing code/nocode OK\n");
+
+ ret=RasmAssemble(AUTOTEST_VAREQU,strlen(AUTOTEST_VAREQU),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (var & equ)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing var & equ OK\n");
+
+ ret=RasmAssemble(AUTOTEST_CHARSET,strlen(AUTOTEST_CHARSET),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (simple charset)\n",cpt);exit(-1);}
+ if (opcodelen!=30 || memcmp(opcode,opcode+5,5) || memcmp(opcode+10,opcode+5,5)) {printf("Autotest %03d ERROR (simple charset)\n",cpt);exit(-1);}
+ if (memcmp(opcode+15,opcode+20,5) || memcmp(opcode+15,opcode+25,5)) {printf("Autotest %03d ERROR (simple charset)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing simple charset OK\n");
+
+ ret=RasmAssemble(AUTOTEST_CHARSET2,strlen(AUTOTEST_CHARSET2),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (extended charset)\n",cpt);exit(-1);}
+ for (i=chk=0;i<opcodelen;i++) chk+=opcode[i];
+ if (opcodelen!=16 || chk!=0x312) {printf("Autotest %03d ERROR (extended charset)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing extended charset OK\n");
+
+ ret=RasmAssemble(AUTOTEST_QUOTES,strlen(AUTOTEST_QUOTES),&opcode,&opcodelen);
+ if (!ret && opcodelen==10 && opcode[5]==0xE4 && opcode[6]==0x0D && opcode[7]==0x64 && opcode[8]==0x0D && opcode[9]==0xE4) {}
+ else {printf("Autotest %03d ERROR (quotes & escaped chars)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing quoted & escaped chars OK\n");
+
+ ret=RasmAssemble(AUTOTEST_NOT,strlen(AUTOTEST_NOT),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (not operator)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing NOT operator OK\n");
+
+ ret=RasmAssemble(AUTOTEST_MACRO,strlen(AUTOTEST_MACRO),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (macro usage)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing macro usage OK\n");
+
+ ret=RasmAssemble(AUTOTEST_ASSERT,strlen(AUTOTEST_ASSERT),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (assert usage)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing assert usage (+prepro regression cases) OK\n");
+
+ ret=RasmAssemble(AUTOTEST_MACROPAR,strlen(AUTOTEST_MACROPAR),&opcode,&opcodelen);
+ if (!ret && opcodelen==12 && memcmp(opcode,"GROUIKgrouik",12)==0) {} else {printf("Autotest %03d ERROR (macro string param)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing macro string parameter OK\n");
+
+ ret=RasmAssemble(AUTOTEST_MACRO_ADV,strlen(AUTOTEST_MACRO_ADV),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (macro param)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing macro parameters OK\n");
+
+ ret=RasmAssemble(AUTOTEST_OVERLOADMACPRM,strlen(AUTOTEST_OVERLOADMACPRM),&opcode,&opcodelen);
+ if (!ret && opcodelen==4 && opcode[0]==1 && opcode[1]==0 && opcode[2]==2 && opcode[3]==1) {} else {printf("Autotest %03d ERROR (macro param overload)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing macro parameter overload OK\n");
+
+ ret=RasmAssemble(AUTOTEST_IFDEFMACRO,strlen(AUTOTEST_IFDEFMACRO),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (ifdef macro)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing IFDEF & MACRO OK\n");
+
+ ret=RasmAssemble(AUTOTEST_LABNUM,strlen(AUTOTEST_LABNUM),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (variables in labels)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing variables in labels OK\n");
+
+ ret=RasmAssemble(AUTOTEST_EQUNUM,strlen(AUTOTEST_EQUNUM),&opcode,&opcodelen);
+ if (!ret && opcodelen==3) {} else {printf("Autotest %03d ERROR (variables in aliases) r=%d l=%d\n",cpt,ret,opcodelen);MiniDump(opcode,opcodelen);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing variables in aliases OK\n");
+
+ ret=RasmAssemble(AUTOTEST_DELAYNUM,strlen(AUTOTEST_DELAYNUM),&opcode,&opcodelen);
+ if (!ret && opcodelen==9 && opcode[0]==6 && opcode[2]==7 && opcode[4]==8) {} else {printf("Autotest %03d ERROR (delayed expr labels) r=%d l=%d\n",cpt,ret,opcodelen);MiniDump(opcode,opcodelen);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing delayed expression labels OK\n");
+
+ ret=RasmAssemble(AUTOTEST_PROXIM,strlen(AUTOTEST_PROXIM),&opcode,&opcodelen);
+ if (!ret && opcode[1]==3) {} else {printf("Autotest %03d ERROR (proximity labels)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing proximity labels OK\n");
+
+ ret=RasmAssemble(AUTOTEST_STRUCT,strlen(AUTOTEST_STRUCT),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (structs)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing structs OK\n");
+
+ ret=RasmAssemble(AUTOTEST_STRUCT2,strlen(AUTOTEST_STRUCT2),&opcode,&opcodelen);
+ if (!ret && opcodelen==1) {} else {printf("Autotest %03d ERROR (sizeof struct fields)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing SIZEOF struct fields OK\n");
+
+ ret=RasmAssemble(AUTOTEST_REPEAT,strlen(AUTOTEST_REPEAT),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (extended repeat)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing REPEAT cases OK\n");
+
+ ret=RasmAssemble(AUTOTEST_REPEATKO,strlen(AUTOTEST_REPEATKO),&opcode,&opcodelen);
+ if (ret) {} else {printf("Autotest %03d ERROR (repeat without end must return error)\n",cpt);exit(-1);}
+printf("testing REPEAT without REND OK\n");
+
+ ret=RasmAssemble(AUTOTEST_WHILEKO,strlen(AUTOTEST_WHILEKO),&opcode,&opcodelen);
+ if (ret) {} else {printf("Autotest %03d ERROR (while without end must return error)\n",cpt);exit(-1);}
+printf("testing WHILE without WEND OK\n");
+
+ ret=RasmAssemble(AUTOTEST_TICKER,strlen(AUTOTEST_TICKER),&opcode,&opcodelen);
+ if (!ret && opcodelen==18) {} else {printf("Autotest %03d ERROR (ticker (re)count)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing ticker OK\n");
+
+ ret=RasmAssemble(AUTOTEST_DEFUSED,strlen(AUTOTEST_DEFUSED),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (ifdef ifused)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing IFDEF / IFUSED OK\n");
+
+ ret=RasmAssemble(AUTOTEST_SAVEINVALID1,strlen(AUTOTEST_SAVEINVALID1),&opcode,&opcodelen);
+ if (ret) {} else {printf("Autotest %03d ERROR (invalid size for SAVE)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing invalid size for SAVE OK\n");
+
+ ret=RasmAssemble(AUTOTEST_SAVEINVALID2,strlen(AUTOTEST_SAVEINVALID2),&opcode,&opcodelen);
+ if (ret) {} else {printf("Autotest %03d ERROR (invalid offset for SAVE)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing invalid offset (too low) for SAVE OK\n");
+
+ ret=RasmAssemble(AUTOTEST_SAVEINVALID3,strlen(AUTOTEST_SAVEINVALID3),&opcode,&opcodelen);
+ if (ret) {} else {printf("Autotest %03d ERROR (invalid offset for SAVE)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing invalid offset (too high) for SAVE OK\n");
+
+ ret=RasmAssemble(AUTOTEST_INHIBITION2,strlen(AUTOTEST_INHIBITION2),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (if switch inhibition)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing if/switch inhibition OK\n");
+
+ ret=RasmAssemble(AUTOTEST_INHIBITIONMAX,strlen(AUTOTEST_INHIBITIONMAX),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (moar inhibition)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing more inhibition cases OK\n");
+
+ ret=RasmAssemble(AUTOTEST_MACROPROX,strlen(AUTOTEST_MACROPROX),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (macro + prox)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing regression case with proximity label following macro definition OK\n");
+
+ ret=RasmAssemble(AUTOTEST_PROXBACK,strlen(AUTOTEST_PROXBACK),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (boucle/macro + prox = recup global)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing global label back as reference when leaving macro and loops OK\n");
+
+ ret=RasmAssemble(AUTOTEST_LOCAPROX,strlen(AUTOTEST_LOCAPROX),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (far local+prox access)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing far access of a proximity label refering to local OK\n");
+
+ ret=RasmAssemble(AUTOTEST_NEGATIVE,strlen(AUTOTEST_NEGATIVE),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (formula case 0)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing formula case 0 OK\n");
+
+ ret=RasmAssemble(AUTOTEST_OPERATORASSIGN,strlen(AUTOTEST_OPERATORASSIGN),&opcode,&opcodelen);
+ if (!ret && opcodelen==1) {} else {printf("Autotest %03d ERROR (operator assignment)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing operator assignment OK\n");
+
+ ret=RasmAssemble(AUTOTEST_OPERATORASSIGN2,strlen(AUTOTEST_OPERATORASSIGN2),&opcode,&opcodelen);
+ if (!ret && opcodelen==1) {} else {printf("Autotest %03d ERROR (operator assignment + repeat)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing operator assignment OK\n");
+
+ ret=RasmAssemble(AUTOTEST_FORMULA1,strlen(AUTOTEST_FORMULA1),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (formula case 1)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing formula case 1 OK\n");
+
+ ret=RasmAssemble(AUTOTEST_SETINSIDE,strlen(AUTOTEST_SETINSIDE),&opcode,&opcodelen);
+ if (ret) {} else {printf("Autotest %03d ERROR (set var inside expression must trigger error)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing var set inside an expression must trigger an error OK\n");
+
+ ret=RasmAssemble(AUTOTEST_CODESKIP,strlen(AUTOTEST_CODESKIP),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (code skip)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing code skip OK\n");
+
+ ret=RasmAssemble(AUTOTEST_FORMULA2,strlen(AUTOTEST_FORMULA2),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (formula case 2 function+multiple parenthesis)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing formula functions + multiple parenthesis OK\n");
+
+ ret=RasmAssemble(AUTOTEST_SHIFTMAX,strlen(AUTOTEST_SHIFTMAX),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (shifting more than 31 must give zero result)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing shifting more than 31 OK\n");
+
+ ret=RasmAssemble(AUTOTEST_PLUSCOLOR,strlen(AUTOTEST_PLUSCOLOR),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (formula func for Plus color management)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing formula function for Plus color management OK\n");
+
+ ret=RasmAssemble(AUTOTEST_FRAC,strlen(AUTOTEST_FRAC),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (formula func FRAC)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing formula FRAC function OK\n");
+
+ ret=RasmAssemble(AUTOTEST_UNDERVAR,strlen(AUTOTEST_UNDERVAR),&opcode,&opcodelen);
+ if (!ret && opcodelen==4) {} else {printf("Autotest %03d ERROR (var starting with _)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing var names starting with '_' OK\n");
+
+ ret=RasmAssemble(AUTOTEST_NOEXPORT,strlen(AUTOTEST_NOEXPORT),&opcode,&opcodelen);
+ if (!ret && opcodelen==2) {} else {printf("Autotest %03d ERROR (noexport/enoexport)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing export/noexport OK\n");
+
+ ret=RasmAssemble(AUTOTEST_ENHANCED_LD,strlen(AUTOTEST_ENHANCED_LD),&opcode,&opcodelen);
+ if (!ret && memcmp(opcode,opcode+opcodelen/2,opcodelen/2)==0) {} else {printf("Autotest %03d ERROR (enhanced LD)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing enhanced LD OK\n");
+
+ ret=RasmAssemble(AUTOTEST_ENHANCED_LD2,strlen(AUTOTEST_ENHANCED_LD2),&opcode,&opcodelen);
+ if (!ret && memcmp(opcode,opcode+opcodelen/2,opcodelen/2)==0) {} else {printf("Autotest %03d ERROR (enhanced LD 2)\n",cpt);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing enhanced LD variante OK\n");
+
+ ret=RasmAssemble(AUTOTEST_ENHANCED_PUSHPOP,strlen(AUTOTEST_ENHANCED_PUSHPOP),&opcode,&opcodelen);
+ if (!ret && memcmp(opcode,opcode+opcodelen/2,opcodelen/2)==0) {} else {printf("Autotest %03d ERROR (enhanced PUSH/POP/NOP)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing enhanced PUSH/POP OK\n");
+
+ ret=RasmAssemble(AUTOTEST_PAGELABELGEN,strlen(AUTOTEST_PAGELABELGEN),&opcode,&opcodelen);
+ if (!ret) {} else {printf("Autotest %03d ERROR (pagelabelgen)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing page tag with generated label name OK\n");
+
+ ret=RasmAssembleInfo(AUTOTEST_EMBEDDED_ERRORS,strlen(AUTOTEST_EMBEDDED_ERRORS),&opcode,&opcodelen,&debug);
+ if (ret && debug->nberror==2 && debug->nbsymbol==3) {
+/*
+ printf("\n");
+ for (i=0;i<debug->nberror;i++) {
+ printf("%d -> %s\n",i,debug->error[i].msg);
+ }
+ for (i=0;i<debug->nbsymbol;i++) {
+ printf("%d -> %s=%d\n",i,debug->symbol[i].name,debug->symbol[i].v);
+ }
+ RasmFreeInfoStruct(debug);
+*/
+ } else {printf("Autotest %03d ERROR (embedded error struct) err=%d nberr=%d (2) nbsymb=%d (3)\n",cpt,ret,debug->nberror,debug->nbsymbol);MiniDump(opcode,opcodelen);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing internal error struct OK\n");
+
+ ret=RasmAssembleInfo(AUTOTEST_EMBEDDED_LABELS,strlen(AUTOTEST_EMBEDDED_LABELS),&opcode,&opcodelen,&debug);
+ if (!ret && debug->nbsymbol==3) {
+ /*
+ printf("\nnbsymbol=%d\n",debug->nbsymbol);
+ for (i=0;i<debug->nbsymbol;i++) {
+ printf("%d -> %s=%d\n",i,debug->symbol[i].name,debug->symbol[i].v);
+ }*/
+ RasmFreeInfoStruct(debug);
+ } else {printf("Autotest %03d ERROR (embedded test)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+printf("testing internal label struct OK\n");
+
+#ifdef RDD
+ printf("\n%d bytes\n",_static_library_memory_used);
+
+ tmpstr3=FileReadContent("./test/PlayerAky.asm",&filelen);
+ printf(".");fflush(stdout);ret=RasmAssembleInfo(tmpstr3,filelen,&opcode,&opcodelen,&debug);
+ if (!ret) {} else {printf("Autotest %03d ERROR (PlayerAky)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);}
+ if (opcode) MemFree(opcode);opcode=NULL;cpt++;
+ RasmFreeInfoStruct(debug);
+ MemFree(tmpstr3);
+
+ printf("\n%d bytes\n",_static_library_memory_used);
+#endif
+
+ FileRemoveIfExists("rasmoutput.cpr");
+
+ printf("All internal tests OK\n");
+ #ifdef RDD
+ /* private dev lib tools */
+printf("checking memory\n");
+ CloseLibrary();
+ #endif
+ exit(0);
+}
+
+
+/******************************************************
+LZ48 v005 / LZ49 v002
+******************************************************/
+int LZ48_encode_extended_length(unsigned char *odata, int length)
+{
+ int ioutput=0;
+
+ while (length>=255) {
+ odata[ioutput++]=0xFF;
+ length-=255;
+ }
+ /* if the last value is 255 we must encode 0 to end extended length */
+ /*if (length==0) rasm_printf(ae,"bugfixed!\n");*/
+ odata[ioutput++]=(unsigned char)length;
+ return ioutput;
+}
+
+int LZ48_encode_block(unsigned char *odata,unsigned char *data, int literaloffset,int literalcpt,int offset,int maxlength)
+{
+ int ioutput=1;
+ int token=0;
+ int i;
+
+ if (offset<0 || offset>255) {
+ fprintf(stderr,"internal offset error!\n");
+ exit(-2);
+ }
+
+ if (literalcpt<15) {
+ token=literalcpt<<4;
+ } else {
+ token=0xF0;
+ ioutput+=LZ48_encode_extended_length(odata+ioutput,literalcpt-15);
+ }
+
+ for (i=0;i<literalcpt;i++) odata[ioutput++]=data[literaloffset++];
+
+ if (maxlength<18) {
+ if (maxlength>2) {
+ token|=(maxlength-3);
+ } else {
+ /* endoffset has no length */
+ }
+ } else {
+ token|=0xF;
+ ioutput+=LZ48_encode_extended_length(odata+ioutput,maxlength-18);
+ }
+
+ odata[ioutput++]=(unsigned char)offset-1;
+
+ odata[0]=(unsigned char)token;
+ return ioutput;
+}
+
+unsigned char *LZ48_encode_legacy(unsigned char *data, int length, int *retlength)
+{
+ int i,startscan,current=1,token,ioutput=1,curscan;
+ int maxoffset=0,maxlength,matchlength,literal=0,literaloffset=1;
+ unsigned char *odata=NULL;
+
+ odata=MemMalloc((size_t)length*1.5+10);
+ if (!odata) {
+ fprintf(stderr,"malloc(%.0lf) - memory full\n",(size_t)length*1.5+10);
+ exit(-1);
+ }
+
+ /* first byte always literal */
+ odata[0]=data[0];
+
+ /* force short data encoding */
+ if (length<5) {
+ token=(length-1)<<4;
+ odata[ioutput++]=(unsigned char)token;
+ for (i=1;i<length;i++) odata[ioutput++]=data[current++];
+ odata[ioutput++]=0xFF;
+ *retlength=ioutput;
+ return odata;
+ }
+
+ while (current<length) {
+ maxlength=0;
+ startscan=current-255;
+ if (startscan<0) startscan=0;
+ while (startscan<current) {
+ matchlength=0;
+ curscan=current;
+ for (i=startscan;curscan<length;i++) {
+ if (data[i]==data[curscan++]) matchlength++; else break;
+ }
+ if (matchlength>=3 && matchlength>maxlength) {
+ maxoffset=startscan;
+ maxlength=matchlength;
+ }
+ startscan++;
+ }
+ if (maxlength) {
+ ioutput+=LZ48_encode_block(odata+ioutput,data,literaloffset,literal,current-maxoffset,maxlength);
+ current+=maxlength;
+ literaloffset=current;
+ literal=0;
+ } else {
+ literal++;
+ current++;
+ }
+ }
+ ioutput+=LZ48_encode_block(odata+ioutput,data,literaloffset,literal,0,0);
+ *retlength=ioutput;
+ return odata;
+}
+
+int LZ49_encode_extended_length(unsigned char *odata, int length)
+{
+ int ioutput=0;
+
+ while (length>=255) {
+ odata[ioutput++]=0xFF;
+ length-=255;
+ }
+ /* if the last value is 255 we must encode 0 to end extended length */
+ /*if (length==0) rasm_printf(ae,"bugfixed!\n");*/
+ odata[ioutput++]=(unsigned char)length;
+ return ioutput;
+}
+
+int LZ49_encode_block(unsigned char *odata,unsigned char *data, int literaloffset,int literalcpt,int offset,int maxlength)
+{
+ int ioutput=1;
+ int token=0;
+ int i;
+
+ if (offset<0 || offset>511) {
+ fprintf(stderr,"internal offset error!\n");
+ exit(-2);
+ }
+
+ if (literalcpt<7) {
+ token=literalcpt<<4;
+ } else {
+ token=0x70;
+ ioutput+=LZ49_encode_extended_length(odata+ioutput,literalcpt-7);
+ }
+
+ for (i=0;i<literalcpt;i++) odata[ioutput++]=data[literaloffset++];
+
+ if (maxlength<18) {
+ if (maxlength>2) {
+ token|=(maxlength-3);
+ } else {
+ /* endoffset has no length */
+ }
+ } else {
+ token|=0xF;
+ ioutput+=LZ49_encode_extended_length(odata+ioutput,maxlength-18);
+ }
+
+ if (offset>255) {
+ token|=0x80;
+ offset-=256;
+ }
+ odata[ioutput++]=(unsigned char)offset-1;
+
+ odata[0]=(unsigned char)token;
+ return ioutput;
+}
+
+unsigned char *LZ49_encode_legacy(unsigned char *data, int length, int *retlength)
+{
+ int i,startscan,current=1,token,ioutput=1,curscan;
+ int maxoffset=0,maxlength,matchlength,literal=0,literaloffset=1;
+ unsigned char *odata=NULL;
+
+ odata=MemMalloc((size_t)(length*1.5+10));
+ if (!odata) {
+ fprintf(stderr,"malloc(%.0lf) - memory full\n",(size_t)length*1.5+10);
+ exit(-1);
+ }
+
+ /* first byte always literal */
+ odata[0]=data[0];
+
+ /* force short data encoding */
+ if (length<5) {
+ token=(length-1)<<4;
+ odata[ioutput++]=(unsigned char)token;
+ for (i=1;i<length;i++) odata[ioutput++]=data[current++];
+ odata[ioutput++]=0xFF;
+ *retlength=ioutput;
+ return odata;
+ }
+
+ while (current<length) {
+ maxlength=0;
+ startscan=current-511;
+ if (startscan<0) startscan=0;
+ while (startscan<current) {
+ matchlength=0;
+ curscan=current;
+ for (i=startscan;curscan<length;i++) {
+ if (data[i]==data[curscan++]) matchlength++; else break;
+ }
+ if (matchlength>=3 && matchlength>maxlength) {
+ maxoffset=startscan;
+ maxlength=matchlength;
+ }
+ startscan++;
+ }
+ if (maxlength) {
+ ioutput+=LZ49_encode_block(odata+ioutput,data,literaloffset,literal,current-maxoffset,maxlength);
+ current+=maxlength;
+ literaloffset=current;
+ literal=0;
+ } else {
+ literal++;
+ current++;
+ }
+ }
+ ioutput+=LZ49_encode_block(odata+ioutput,data,literaloffset,literal,0,0);
+ *retlength=ioutput;
+ return odata;
+}
+
+
+/***************************************
+ semi-generic body of program
+***************************************/
+
+#ifndef INTEGRATED_ASSEMBLY
+
+/*
+ Usage
+ display the mandatory parameters
+*/
+void Usage(int help)
+{
+ #undef FUNC
+ #define FUNC "Usage"
+
+ printf("%s (c) 2017 Edouard BERGE (use -n option to display all licenses)\n",RASM_VERSION);
+ #ifndef NO_3RD_PARTIES
+ printf("LZ4 (c) Yann Collet / ZX7 (c) Einar Saukas / Exomizer 2 (c) Magnus Lind\n");
+ #endif
+ printf("\n");
+ printf("SYNTAX: rasm <inputfile> [options]\n");
+ printf("\n");
+
+ if (help) {
+ printf("FILENAMES:\n");
+ printf("-oa automatic radix from input filename\n");
+ printf("-o <outputfile radix> choose a common radix for all files\n");
+ printf("-ob <binary filename> choose a full filename for binary output\n");
+ printf("-oc <cartridge filename> choose a full filename for cartridge output\n");
+ printf("-oi <snapshot filename> choose a full filename for snapshot output\n");
+ printf("-os <symbol filename> choose a full filename for symbol output\n");
+ printf("-ot <tape filename> choose a full filename for tape output\n");
+ printf("-ok <breakpoint filename>choose a full filename for breakpoint output\n");
+ printf("-I<path> set a path for files to read\n");
+ printf("-no disable all file output\n");
+ printf("DEPENDENCIES EXPORT:\n");
+ printf("-depend=make output dependencies on a single line\n");
+ printf("-depend=list output dependencies as a list\n");
+ printf("if 'binary filename' is set then it will be outputed first\n");
+ printf("SYMBOLS EXPORT:\n");
+ printf("-s export symbols %%s #%%X B%%d (label,adr,cprbank)\n");
+ printf("-sz export symbols with ZX emulator convention\n");
+ printf("-sp export symbols with Pasmo convention\n");
+ printf("-sw export symbols with Winape convention\n");
+ printf("-ss export symbols in the snapshot (SYMB chunk for ACE)\n");
+ printf("-sc <format> export symbols with source code convention\n");
+ printf("-sm export symbol in multiple files (one per bank)\n");
+ printf("-l <labelfile> import symbol file (winape,pasmo,rasm)\n");
+ printf("-eb export breakpoints\n");
+ printf("-wu warn for unused symbols (alias, var or label)\n");
+ printf("SYMBOLS ADDITIONAL OPTIONS:\n");
+ printf("-sl export also local symbol\n");
+ printf("-sv export also variables symbol\n");
+ printf("-sq export also EQU symbol\n");
+ printf("-sa export all symbols (like -sl -sv -sq option)\n");
+ printf("-Dvariable=value import value for variable\n");
+ printf("COMPATIBILITY:\n");
+ printf("-m Maxam style calculations\n");
+ printf("-dams Dams 'dot' label convention\n");
+ printf("-ass AS80 behaviour mimic\n");
+ printf("-uz UZ80 behaviour mimic\n");
+
+ printf("EDSK generation/update:\n");
+ printf("-eo overwrite files on disk if it already exists\n");
+ printf("SNAPSHOT:\n");
+ printf("-sb export breakpoints in snapshot (BRKS & BRKC chunks)\n");
+ printf("-ss export symbols in the snapshot (SYMB chunk for ACE)\n");
+ printf("-v2 export snapshot version 2 instead of version 3\n");
+ printf("PARSING:\n");
+ printf("-me <value> set maximum number of error (0 means no limit)\n");
+ printf("-xr extended error display\n");
+ printf("-w disable warnings\n");
+ printf("-void force void usage with macro without parameter\n");
+ printf("\n");
+ } else {
+ printf("use option -h for help\n");
+ printf("\n");
+ }
+
+ exit(ABORT_ERROR);
+}
+
+void Licenses()
+{
+ #undef FUNC
+ #define FUNC "Licenses"
+
+printf(" ____ \n");
+printf(" | _ \\ __ _ ___ _ __ ___ \n");
+printf(" | |_) / _` / __| '_ ` _ \\ \n");
+printf(" | _ < (_| \\__ \\ | | | | |\n");
+printf(" |_| \\_\\__,_|___/_| |_| |_|\n");
+printf("\n");
+printf(" is using MIT 'expat' license\n");
+printf("\" Copyright (c) BERGE Edouard (roudoudou)\n\n");
+
+printf("Permission is hereby granted, free of charge,\n");
+printf("to any person obtaining a copy of this software\n");
+printf("and associated documentation/source files of\n");
+printf("RASM, to deal in the Software without restriction,\n");
+printf("including without limitation the rights to use,\n");
+printf("copy, modify, merge, publish, distribute,\n");
+printf("sublicense, and/or sell copies of the Software,\n");
+printf("and to permit persons to whom the Software is\n");
+printf("furnished to do so, subject to the following\n");
+printf("conditions:\n");
+
+printf("The above copyright notice and this permission\n");
+printf("notice shall be included in all copies or\n");
+printf("substantial portions of the Software.\n");
+printf("The Software is provided 'as is', without\n");
+printf("warranty of any kind, express or implied,\n");
+printf("including but not limited to the warranties of\n");
+printf("merchantability, fitness for a particular\n");
+printf("purpose and noninfringement. In no event shall\n");
+printf("the authors or copyright holders be liable for\n");
+printf("any claim, damages or other liability, whether\n");
+printf("in an action of contract, tort or otherwise,\n");
+printf("arising from, out of or in connection with the\n");
+printf("software or the use or other dealings in the\n");
+printf("Software. \"\n");
+
+#ifndef NO_3RD_PARTIES
+printf("\n\n\n\n");
+printf("******* license of LZ4 cruncher / sources were modified ***********\n\n\n\n");
+
+printf("BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\n");
+
+printf("Redistribution and use in source and binary forms, with or without\n");
+printf("modification, are permitted provided that the following conditions are\n");
+printf("met:\n\n");
+
+printf(" * Redistributions of source code must retain the above copyright\n");
+printf("notice, this list of conditions and the following disclaimer.\n");
+printf(" * Redistributions in binary form must reproduce the above\n");
+printf("copyright notice, this list of conditions and the following disclaimer\n");
+printf("in the documentation and/or other materials provided with the\n");
+printf("distribution.\n\n");
+
+printf("THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n");
+printf("'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n");
+printf("LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n");
+printf("A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n");
+printf("OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n");
+printf("SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n");
+printf("LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n");
+printf("DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n");
+printf("THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n");
+printf("(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n");
+printf("OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n");
+
+printf("You can contact the author at :\n");
+printf(" - LZ4 homepage : http://www.lz4.org\n");
+printf(" - LZ4 source repository : https://github.com/lz4/lz4\n");
+
+
+printf("\n\n\n\n");
+printf("******* license of ZX7 cruncher / sources were modified ***********\n\n\n\n");
+
+
+printf(" * (c) Copyright 2012 by Einar Saukas. All rights reserved.\n");
+printf(" *\n");
+printf(" * Redistribution and use in source and binary forms, with or without\n");
+printf(" * modification, are permitted provided that the following conditions are met:\n");
+printf(" * * Redistributions of source code must retain the above copyright\n");
+printf(" * notice, this list of conditions and the following disclaimer.\n");
+printf(" * * Redistributions in binary form must reproduce the above copyright\n");
+printf(" * notice, this list of conditions and the following disclaimer in the\n");
+printf(" * documentation and/or other materials provided with the distribution.\n");
+printf(" * * The name of its author may not be used to endorse or promote products\n");
+printf(" * derived from this software without specific prior written permission.\n");
+printf(" *\n");
+printf(" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND\n");
+printf(" * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n");
+printf(" * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n");
+printf(" * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY\n");
+printf(" * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n");
+printf(" * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n");
+printf(" * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n");
+printf(" * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n");
+printf(" * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n");
+printf(" * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n");
+
+
+printf("\n\n\n\n");
+printf("******* license of exomizer cruncher / sources were modified ***********\n\n\n\n");
+
+
+printf(" * Copyright (c) 2005 Magnus Lind.\n");
+printf(" *\n");
+printf(" * This software is provided 'as-is', without any express or implied warranty.\n");
+printf(" * In no event will the authors be held liable for any damages arising from\n");
+printf(" * the use of this software.\n");
+printf(" *\n");
+printf(" * Permission is granted to anyone to use this software, alter it and re-\n");
+printf(" * distribute it freely for any non-commercial, non-profit purpose subject to\n");
+printf(" * the following restrictions:\n");
+printf(" *\n");
+printf(" * 1. The origin of this software must not be misrepresented; you must not\n");
+printf(" * claim that you wrote the original software. If you use this software in a\n");
+printf(" * product, an acknowledgment in the product documentation would be\n");
+printf(" * appreciated but is not required.\n");
+printf(" *\n");
+printf(" * 2. Altered source versions must be plainly marked as such, and must not\n");
+printf(" * be misrepresented as being the original software.\n");
+printf(" *\n");
+printf(" * 3. This notice may not be removed or altered from any distribution.\n");
+printf(" *\n");
+printf(" * 4. The names of this software and/or it's copyright holders may not be\n");
+printf(" * used to endorse or promote products derived from this software without\n");
+printf(" * specific prior written permission.\n");
+#endif
+
+printf("\n\n");
+
+
+
+ exit(0);
+}
+
+int _internal_check_flexible(char *fxp) {
+ #undef FUNC
+ #define FUNC "_internal_check_flexible"
+
+ char *posval,*posvar;
+ int cpt=0,i;
+
+ posvar=strstr(fxp,"%s");
+ posval=strstr(fxp,"%");
+ if (!posval || !posvar) {
+ printf("invalid flexible export string, need 2 formated fields, example: \"%%s %%d\"\n");
+ exit(1);
+ }
+ if (posval<posvar) {
+ printf("invalid flexible export string, must be %%s before the %% for value, example: \"%%s %%d\"\n");
+ exit(1);
+ }
+ for (i=0;fxp[i];i++) if (fxp[i]=='%') cpt++;
+ if (cpt>2) {
+ printf("invalid flexible export string, must be only two formated fields, example: \"%%s %%d\"\n");
+ exit(1);
+ }
+
+ return 1;
+}
+/*
+ ParseOptions
+
+ used to parse command line and configuration file
+*/
+int ParseOptions(char **argv,int argc, struct s_parameter *param)
+{
+ #undef FUNC
+ #define FUNC "ParseOptions"
+
+ char *sep;
+ int i=0;
+
+ if (strcmp(argv[i],"-autotest")==0) {
+ RasmAutotest();
+ } else if (strcmp(argv[i],"-uz")==0) {
+ param->as80=2;
+ } else if (strcmp(argv[i],"-ass")==0) {
+ param->as80=1;
+ } else if (strcmp(argv[i],"-eb")==0) {
+ param->export_brk=1;
+ } else if (strcmp(argv[i],"-wu")==0) {
+ param->warn_unused=1;
+ } else if (strcmp(argv[i],"-dams")==0) {
+ } else if (strcmp(argv[i],"-void")==0) {
+ param->macrovoid=1;
+ } else if (strcmp(argv[i],"-xr")==0) {
+ param->extended_error=1;
+ } else if (strcmp(argv[i],"-eo")==0) {
+ param->edskoverwrite=1;
+ } else if (strcmp(argv[i],"-depend=make")==0) {
+ param->dependencies=E_DEPENDENCIES_MAKE;
+ param->checkmode=1;
+ } else if (strcmp(argv[i],"-depend=list")==0) {
+ param->dependencies=E_DEPENDENCIES_LIST;
+ param->checkmode=1;
+ } else if (strcmp(argv[i],"-no")==0) {
+ param->checkmode=1;
+ } else if (strcmp(argv[i],"-w")==0) {
+ param->nowarning=1;
+ } else if (argv[i][0]=='-') {
+ switch(argv[i][1])
+ {
+ case 'I':
+ if (argv[i][2]) {
+ char *curpath;
+ int l;
+ l=strlen(argv[i]);
+ curpath=MemMalloc(l); /* strlen(path)+2 */
+ strcpy(curpath,argv[i]+2);
+#ifdef OS_WIN
+ if (argv[i][l-1]!='/' && argv[i][l-1]!='\\') strcat(curpath,"\\");
+#else
+ if (argv[i][l-1]!='/' && argv[i][l-1]!='\\') strcat(curpath,"/");
+#endif
+ FieldArrayAddDynamicValueConcat(&param->pathdef,&param->npath,&param->mpath,curpath);
+ MemFree(curpath);
+ } else {
+ Usage(1);
+ }
+ break;
+ case 'D':
+ if ((sep=strchr(argv[i],'='))!=NULL) {
+ if (sep!=argv[i]+2) {
+ FieldArrayAddDynamicValueConcat(&param->symboldef,&param->nsymb,&param->msymb,argv[i]+2);
+ } else {
+ Usage(1);
+ }
+ } else {
+ Usage(1);
+ }
+ break;
+ case 'm':
+ switch (argv[i][2]) {
+ case 0:
+ param->rough=0.0;
+ return i;
+ case 'e':
+ if (argv[i][3]) Usage(1);
+ if (i+1<argc) {
+ param->maxerr=atoi(argv[++i]);
+ return i;
+ }
+ default:Usage(1);
+ }
+ Usage(1);
+ break;
+ case 's':
+ if (argv[i][2] && argv[i][3]) Usage(1);
+ switch (argv[i][2]) {
+ case 0:param->export_sym=1;return 0;
+ case 'z':
+ param->export_sym=5;return 0;
+ case 'm':
+ param->export_multisym=1;return 0;
+ case 'b':
+ param->export_snabrk=1;return 0;
+ case 'p':
+ param->export_sym=2;return 0;
+ case 'w':
+ param->export_sym=3;return 0;
+ case 'c':
+ if (i+1<argc) {
+ param->export_sym=4;
+ param->flexible_export=TxtStrDup(argv[++i]);
+ param->flexible_export=MemRealloc(param->flexible_export,strlen(param->flexible_export)+3);
+ strcat(param->flexible_export,"\n");
+ /* check export string */
+ if (_internal_check_flexible(param->flexible_export)) return i; else Usage(1);
+ }
+ Usage(1);
+ case 'l':
+ param->export_local=1;return 0;
+ case 'v':
+ param->export_var=1;return 0;
+ case 'q':
+ param->export_equ=1;return 0;
+ case 'a':
+ param->export_local=1;
+ param->export_var=1;
+ param->export_equ=1;
+ return 0;
+ case 's':
+ param->export_local=1;
+ param->export_sym=1;
+ param->export_sna=1;return 0;
+ default:
+ break;
+ }
+ Usage(1);
+ case 'l':
+ if (argv[i][2]) Usage(1);
+ if (i+1<argc) {
+ FieldArrayAddDynamicValue(&param->labelfilename,argv[++i]);
+ break;
+ }
+ Usage(1);
+ case 'i':
+printf("@@@\n@@@ --> deprecated option, there is no need to use -i option to set input file <--\n@@@\n");
+ Usage(0);
+ case 'o':
+ if (argv[i][2] && argv[i][3]) Usage(1);
+ switch (argv[i][2]) {
+ case 0:
+ if (i+1<argc && param->outputfilename==NULL) {
+ param->outputfilename=argv[++i];
+ break;
+ }
+ Usage(1);
+ case 'a':
+ param->automatic_radix=1;
+ break;
+ case 't':
+ if (i+1<argc && param->tape_name==NULL) {
+ param->tape_name=argv[++i];
+ break;
+ }
+ Usage(1);
+ case 'i':
+ if (i+1<argc && param->snapshot_name==NULL) {
+ param->snapshot_name=argv[++i];
+ break;
+ }
+ Usage(1);
+ case 'b':
+ if (i+1<argc && param->binary_name==NULL) {
+ param->binary_name=argv[++i];
+ break;
+ }
+ Usage(1);
+ case 'c':
+ if (i+1<argc && param->cartridge_name==NULL) {
+ param->cartridge_name=argv[++i];
+ break;
+ }
+ Usage(1);
+ case 'k':
+ if (i+1<argc && param->breakpoint_name==NULL) {
+ param->breakpoint_name=argv[++i];
+ break;
+ }
+ Usage(1);
+ case 's':
+ if (i+1<argc && param->symbol_name==NULL) {
+ param->symbol_name=argv[++i];
+ break;
+ }
+ Usage(1);
+ default:
+ Usage(1);
+ }
+ break;
+ case 'd':if (!argv[i][2]) printf("deprecated option -d\n"); else Usage(1);
+ break;
+ case 'a':if (!argv[i][2]) printf("deprecated option -a\n"); else Usage(1);
+ break;
+ case 'c':if (!argv[i][2]) printf("deprecated option -c\n"); else Usage(1);
+ break;
+ case 'v':
+ if (!argv[i][2]) {
+ printf("deprecated option -v\n");
+ } else if (argv[i][2]=='2') {
+ param->v2=1;
+ }
+ break;
+ case 'n':if (!argv[i][2]) Licenses(); else Usage(1);
+ case 'h':Usage(1);
+ default:
+ Usage(1);
+ }
+ } else {
+ if (param->filename==NULL) {
+ param->filename=TxtStrDup(argv[i]);
+ } else if (param->outputfilename==NULL) {
+ param->outputfilename=argv[i];
+ } else Usage(1);
+ }
+ return i;
+}
+
+/*
+ GetParametersFromCommandLine
+ retrieve parameters from command line and fill pointers to file names
+*/
+void GetParametersFromCommandLine(int argc, char **argv, struct s_parameter *param)
+{
+ #undef FUNC
+ #define FUNC "GetParametersFromCommandLine"
+ int i;
+
+ for (i=1;i<argc;i++)
+ i+=ParseOptions(&argv[i],argc-i,param);
+
+ if (!param->filename) Usage(0);
+ if (param->export_local && !param->export_sym) Usage(1); // à revoir?
+}
+
+/*
+ main
+
+ check parameters
+ execute the main processing
+*/
+
+
+int main(int argc, char **argv)
+{
+ #undef FUNC
+ #define FUNC "main"
+
+ struct s_parameter param={0};
+ int ret;
+
+ param.maxerr=20;
+ param.rough=0.5;
+
+ GetParametersFromCommandLine(argc,argv,&param);
+ ret=Rasm(&param);
+ #ifdef RDD
+ /* private dev lib tools */
+printf("checking memory\n");
+ CloseLibrary();
+ #endif
+ exit(ret);
+ return 0; // Open WATCOM Warns without this...
+}
+
+#endif
+
+
diff --git a/tools/rasm/zx7.h b/tools/rasm/zx7.h
new file mode 100644
index 0000000..f03659d
--- /dev/null
+++ b/tools/rasm/zx7.h
@@ -0,0 +1,293 @@
+/*
+
+Warning! This is a modified version of original sources!
+
+To sum up:
+- all include files and C sources were merged in a single file
+- existing logs were removed (except error logs)
+- main were removed and wrapper added
+
+
+ * (c) Copyright 2012 by Einar Saukas. 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 <COPYRIGHT HOLDER> 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.
+ */
+
+#define ZX7_MAX_OFFSET 2176 /* range 1..2176 */
+#define ZX7_MAX_LEN 65536 /* range 2..65536 */
+
+typedef struct match_t {
+ size_t index;
+ struct match_t *next;
+} Match;
+
+typedef struct optimal_t {
+ size_t bits;
+ int offset;
+ int len;
+} Optimal;
+
+Optimal *optimize(unsigned char *input_data, size_t input_size);
+
+unsigned char *ZX7_compress(Optimal *optimal, unsigned char *input_data, size_t input_size, size_t *output_size);
+
+/*
+ * (c) Copyright 2012 by Einar Saukas. 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 <COPYRIGHT HOLDER> 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.
+ */
+
+int elias_gamma_bits(int value) {
+ int bits;
+
+ bits = 1;
+ while (value > 1) {
+ bits += 2;
+ value >>= 1;
+ }
+ return bits;
+}
+
+int count_bits(int offset, int len) {
+ return 1 + (offset > 128 ? 12 : 8) + elias_gamma_bits(len-1);
+}
+
+Optimal* optimize(unsigned char *input_data, size_t input_size) {
+ size_t *min;
+ size_t *max;
+ Match *matches;
+ Match *match_slots;
+ Optimal *optimal;
+ Match *match;
+ int match_index;
+ int offset;
+ size_t len;
+ size_t best_len;
+ size_t bits;
+ size_t i;
+
+ /* allocate all data structures at once */
+ min = (size_t *)calloc(ZX7_MAX_OFFSET+1, sizeof(size_t));
+ max = (size_t *)calloc(ZX7_MAX_OFFSET+1, sizeof(size_t));
+ matches = (Match *)calloc(256*256, sizeof(Match));
+ match_slots = (Match *)calloc(input_size, sizeof(Match));
+ optimal = (Optimal *)calloc(input_size, sizeof(Optimal));
+
+ if (!min || !max || !matches || !match_slots || !optimal) {
+ fprintf(stderr, "Error: Insufficient memory\n");
+ exit(1);
+ }
+
+ /* first byte is always literal */
+ optimal[0].bits = 8;
+
+ /* process remaining bytes */
+ for (i = 1; i < input_size; i++) {
+
+ optimal[i].bits = optimal[i-1].bits + 9;
+ match_index = input_data[i-1] << 8 | input_data[i];
+ best_len = 1;
+ for (match = &matches[match_index]; match->next != NULL && best_len < ZX7_MAX_LEN; match = match->next) {
+ offset = i - match->next->index;
+ if (offset > ZX7_MAX_OFFSET) {
+ match->next = NULL;
+ break;
+ }
+
+ for (len = 2; len <= ZX7_MAX_LEN; len++) {
+ if (len > best_len) {
+ best_len = len;
+ bits = optimal[i-len].bits + count_bits(offset, len);
+ if (optimal[i].bits > bits) {
+ optimal[i].bits = bits;
+ optimal[i].offset = offset;
+ optimal[i].len = len;
+ }
+ } else if (i+1 == max[offset]+len && max[offset] != 0) {
+ len = i-min[offset];
+ if (len > best_len) {
+ len = best_len;
+ }
+ }
+ if (i < offset+len || input_data[i-len] != input_data[i-len-offset]) {
+ break;
+ }
+ }
+ min[offset] = i+1-len;
+ max[offset] = i;
+ }
+ match_slots[i].index = i;
+ match_slots[i].next = matches[match_index].next;
+ matches[match_index].next = &match_slots[i];
+ }
+
+ /* save time by releasing the largest block only, the O.S. will clean everything else later */
+ free(match_slots);
+
+ return optimal;
+}
+
+/*
+ * (c) Copyright 2012 by Einar Saukas. 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 <COPYRIGHT HOLDER> 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.
+ */
+
+unsigned char* output_data;
+size_t output_index;
+size_t bit_index;
+int bit_mask;
+
+void write_byte(int value) {
+ output_data[output_index++] = value;
+}
+
+void write_bit(int value) {
+ if (bit_mask == 0) {
+ bit_mask = 128;
+ bit_index = output_index;
+ write_byte(0);
+ }
+ if (value > 0) {
+ output_data[bit_index] |= bit_mask;
+ }
+ bit_mask >>= 1;
+}
+
+void write_elias_gamma(int value) {
+ int i;
+
+ for (i = 2; i <= value; i <<= 1) {
+ write_bit(0);
+ }
+ while ((i >>= 1) > 0) {
+ write_bit(value & i);
+ }
+}
+
+unsigned char *ZX7_compress(Optimal *optimal, unsigned char *input_data, size_t input_size, size_t *output_size) {
+ size_t input_index;
+ size_t input_prev;
+ int offset1;
+ int mask;
+ int i;
+
+ /* calculate and allocate output buffer */
+ input_index = input_size-1;
+ *output_size = (optimal[input_index].bits+18+7)/8;
+ output_data = (unsigned char *)MemMalloc(*output_size);
+
+ /* un-reverse optimal sequence */
+ optimal[input_index].bits = 0;
+ while (input_index > 0) {
+ input_prev = input_index - (optimal[input_index].len > 0 ? optimal[input_index].len : 1);
+ optimal[input_prev].bits = input_index;
+ input_index = input_prev;
+ }
+
+ output_index = 0;
+ bit_mask = 0;
+
+ /* first byte is always literal */
+ write_byte(input_data[0]);
+
+ /* process remaining bytes */
+ while ((input_index = optimal[input_index].bits) > 0) {
+ if (optimal[input_index].len == 0) {
+
+ /* literal indicator */
+ write_bit(0);
+
+ /* literal value */
+ write_byte(input_data[input_index]);
+
+ } else {
+
+ /* sequence indicator */
+ write_bit(1);
+
+ /* sequence length */
+ write_elias_gamma(optimal[input_index].len-1);
+
+ /* sequence offset */
+ offset1 = optimal[input_index].offset-1;
+ if (offset1 < 128) {
+ write_byte(offset1);
+ } else {
+ offset1 -= 128;
+ write_byte((offset1 & 127) | 128);
+ for (mask = 1024; mask > 127; mask >>= 1) {
+ write_bit(offset1 & mask);
+ }
+ }
+ }
+ }
+
+ /* sequence indicator */
+ write_bit(1);
+
+ /* end marker > MAX_LEN */
+ for (i = 0; i < 16; i++) {
+ write_bit(0);
+ }
+ write_bit(1);
+
+ return output_data;
+}